← back to Cli Printing Press
fix(cli): match both singular and plural required-flag cobra outputs (#1413)
f5ea270df7583160348d1d4f83fe4798fbde153b · 2026-05-16 13:25:49 -0400 · klatt42
isCobraUsageError matched only the plural cobra form (`required flag(s)
"foo" not set`). Cobra emits the SINGULAR form (`required flag "foo"
not set`, no parens) when exactly one MarkFlagRequired flag is missing
on the command, and only switches to plural when multiple are missing
simultaneously. The single-flag case is by far the more common one in
practice.
Result before this fix: a CLI with one required flag, invoked without
it, exits 1 instead of the documented exit-2 usage convention from
#1194. The unknown-flag / unknown-command / flag-needs-argument paths
all worked correctly; only this single edge case fell through.
Adds the singular pattern alongside the plural one. Both stay anchored
to the literal trailing-space-quote (`required flag "`) so application
errors mentioning "required flag" as prose still classify correctly
(the prose-as-non-usage negative test continues to pass).
Discovered while regenerating rok-brain-cli against v4.6.1 — the
single-required-flag case in Brain's `query --query "..."` command
surfaced the gap because `query` has exactly one MarkFlagRequired flag.
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Files touched
M internal/generator/templates/root.go.tmplM internal/generator/templates/root_test.go.tmplM testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/root_test.goM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root.goM testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root_test.go
Diff
commit f5ea270df7583160348d1d4f83fe4798fbde153b
Author: klatt42 <134792614+klatt42@users.noreply.github.com>
Date: Sat May 16 13:25:49 2026 -0400
fix(cli): match both singular and plural required-flag cobra outputs (#1413)
isCobraUsageError matched only the plural cobra form (`required flag(s)
"foo" not set`). Cobra emits the SINGULAR form (`required flag "foo"
not set`, no parens) when exactly one MarkFlagRequired flag is missing
on the command, and only switches to plural when multiple are missing
simultaneously. The single-flag case is by far the more common one in
practice.
Result before this fix: a CLI with one required flag, invoked without
it, exits 1 instead of the documented exit-2 usage convention from
#1194. The unknown-flag / unknown-command / flag-needs-argument paths
all worked correctly; only this single edge case fell through.
Adds the singular pattern alongside the plural one. Both stay anchored
to the literal trailing-space-quote (`required flag "`) so application
errors mentioning "required flag" as prose still classify correctly
(the prose-as-non-usage negative test continues to pass).
Discovered while regenerating rok-brain-cli against v4.6.1 — the
single-required-flag case in Brain's `query --query "..."` command
surfaced the gap because `query` has exactly one MarkFlagRequired flag.
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
---
internal/generator/templates/root.go.tmpl | 11 ++++++++++-
internal/generator/templates/root_test.go.tmpl | 3 ++-
.../printing-press-rich-auth/internal/cli/root_test.go | 3 ++-
.../printing-press-golden/internal/cli/root.go | 11 ++++++++++-
.../printing-press-golden/internal/cli/root_test.go | 3 ++-
5 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/internal/generator/templates/root.go.tmpl b/internal/generator/templates/root.go.tmpl
index 66cc0807..4c69d04e 100644
--- a/internal/generator/templates/root.go.tmpl
+++ b/internal/generator/templates/root.go.tmpl
@@ -114,10 +114,18 @@ func Execute() error {
// - "unknown flag: --foo" (pflag)
// - "unknown shorthand flag: 'x' in -x" (pflag)
// - "unknown command \"foo\" for ..." (Cobra)
-// - "required flag(s) \"foo\" not set" (Cobra MarkFlagRequired)
+// - "required flag \"foo\" not set" (Cobra, single missing)
+// - "required flag(s) \"foo\" not set" (Cobra, multiple missing)
// - "flag needs an argument: --foo" (pflag, missing value)
// - "invalid argument \"x\" for \"--y\" flag: ..." (pflag, parse failure)
//
+// Cobra emits the singular form ("required flag") when exactly one
+// MarkFlagRequired flag is missing, and the plural form ("required
+// flag(s)") only when multiple are missing on the same command. Both
+// shapes must be anchored to avoid matching app-level errors that
+// happen to mention "required flag" as prose; the trailing space + quote
+// (`required flag "`) is the literal punctuation cobra emits.
+//
// Returns false for nil err.
func isCobraUsageError(err error) bool {
if err == nil {
@@ -127,6 +135,7 @@ func isCobraUsageError(err error) bool {
return strings.HasPrefix(msg, "unknown flag") ||
strings.HasPrefix(msg, "unknown shorthand flag") ||
strings.HasPrefix(msg, "unknown command") ||
+ strings.HasPrefix(msg, `required flag "`) ||
strings.HasPrefix(msg, `required flag(s) "`) ||
strings.HasPrefix(msg, "flag needs an argument:") ||
strings.HasPrefix(msg, `invalid argument "`)
diff --git a/internal/generator/templates/root_test.go.tmpl b/internal/generator/templates/root_test.go.tmpl
index b1cd048e..b223eb61 100644
--- a/internal/generator/templates/root_test.go.tmpl
+++ b/internal/generator/templates/root_test.go.tmpl
@@ -24,7 +24,8 @@ func TestIsCobraUsageError(t *testing.T) {
{"unknown flag", errors.New("unknown flag: --foob"), true},
{"unknown shorthand flag", errors.New("unknown shorthand flag: 'x' in -x"), true},
{"unknown command", errors.New("unknown command \"foo\" for \"cli\""), true},
- {"required flag", errors.New("required flag(s) \"query\" not set"), true},
+ {"required flag (singular)", errors.New("required flag \"query\" not set"), true},
+ {"required flag(s) (plural)", errors.New("required flag(s) \"query\", \"vault\" not set"), true},
{"flag needs argument", errors.New("flag needs an argument: --query"), true},
{"invalid argument", errors.New("invalid argument \"abc\" for \"--limit\" flag: strconv.ParseInt: parsing \"abc\": invalid syntax"), true},
// Non-usage errors must NOT be flagged — they should retain their
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/root_test.go b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/root_test.go
index e5336e44..13072f24 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/root_test.go
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/root_test.go
@@ -24,7 +24,8 @@ func TestIsCobraUsageError(t *testing.T) {
{"unknown flag", errors.New("unknown flag: --foob"), true},
{"unknown shorthand flag", errors.New("unknown shorthand flag: 'x' in -x"), true},
{"unknown command", errors.New("unknown command \"foo\" for \"cli\""), true},
- {"required flag", errors.New("required flag(s) \"query\" not set"), true},
+ {"required flag (singular)", errors.New("required flag \"query\" not set"), true},
+ {"required flag(s) (plural)", errors.New("required flag(s) \"query\", \"vault\" not set"), true},
{"flag needs argument", errors.New("flag needs an argument: --query"), true},
{"invalid argument", errors.New("invalid argument \"abc\" for \"--limit\" flag: strconv.ParseInt: parsing \"abc\": invalid syntax"), true},
// Non-usage errors must NOT be flagged — they should retain their
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root.go
index 5c19c268..ca7494e9 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root.go
@@ -108,10 +108,18 @@ func Execute() error {
// - "unknown flag: --foo" (pflag)
// - "unknown shorthand flag: 'x' in -x" (pflag)
// - "unknown command \"foo\" for ..." (Cobra)
-// - "required flag(s) \"foo\" not set" (Cobra MarkFlagRequired)
+// - "required flag \"foo\" not set" (Cobra, single missing)
+// - "required flag(s) \"foo\" not set" (Cobra, multiple missing)
// - "flag needs an argument: --foo" (pflag, missing value)
// - "invalid argument \"x\" for \"--y\" flag: ..." (pflag, parse failure)
//
+// Cobra emits the singular form ("required flag") when exactly one
+// MarkFlagRequired flag is missing, and the plural form ("required
+// flag(s)") only when multiple are missing on the same command. Both
+// shapes must be anchored to avoid matching app-level errors that
+// happen to mention "required flag" as prose; the trailing space + quote
+// (`required flag "`) is the literal punctuation cobra emits.
+//
// Returns false for nil err.
func isCobraUsageError(err error) bool {
if err == nil {
@@ -121,6 +129,7 @@ func isCobraUsageError(err error) bool {
return strings.HasPrefix(msg, "unknown flag") ||
strings.HasPrefix(msg, "unknown shorthand flag") ||
strings.HasPrefix(msg, "unknown command") ||
+ strings.HasPrefix(msg, `required flag "`) ||
strings.HasPrefix(msg, `required flag(s) "`) ||
strings.HasPrefix(msg, "flag needs an argument:") ||
strings.HasPrefix(msg, `invalid argument "`)
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root_test.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root_test.go
index e5336e44..13072f24 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root_test.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/root_test.go
@@ -24,7 +24,8 @@ func TestIsCobraUsageError(t *testing.T) {
{"unknown flag", errors.New("unknown flag: --foob"), true},
{"unknown shorthand flag", errors.New("unknown shorthand flag: 'x' in -x"), true},
{"unknown command", errors.New("unknown command \"foo\" for \"cli\""), true},
- {"required flag", errors.New("required flag(s) \"query\" not set"), true},
+ {"required flag (singular)", errors.New("required flag \"query\" not set"), true},
+ {"required flag(s) (plural)", errors.New("required flag(s) \"query\", \"vault\" not set"), true},
{"flag needs argument", errors.New("flag needs an argument: --query"), true},
{"invalid argument", errors.New("invalid argument \"abc\" for \"--limit\" flag: strconv.ParseInt: parsing \"abc\": invalid syntax"), true},
// Non-usage errors must NOT be flagged — they should retain their
← 43c90ba0 feat(catalog): add OpenRouteService under new maps category
·
back to Cli Printing Press
·
fix(cli): validate-narrative splits piped recipes and strips 62838d04 →