[object Object]

← back to Cli Printing Press

fix(cli): emit structured JSON error from parents invoked without a subcommand in --agent mode (#1373)

fb9125e86965b875d878c2b8460087b8656a896b · 2026-05-14 01:06:03 -0700 · Trevin Chow

* fix(cli): emit structured JSON error from parents invoked without a subcommand in --agent / --json mode

Generated parent commands (resource parents and the framework auth /
profile / jobs / workflow / share groupings) had no RunE wired, so
cobra fell through to its default help renderer. In --agent / --json
mode that put human-readable help on stdout and exited 0 — agents
piping the output through `jq` got a parse error with no signal that
the invocation was incomplete.

Adds a shared parentNoSubcommandRunE helper in helpers.go.tmpl that
emits {"error":"subcommand required","valid_subcommands":[...]} on
stdout and returns usageErr (exit 2) when flags.asJSON is set, falling
back to cmd.Help() for human mode. Wired into command_parent.go.tmpl
and the framework parents that shared the same shape.

Human-mode behavior is unchanged: help still renders, exit 0.

Closes #1138

* test(cli): extend parent-RunE wiring test to cover workflow and share parents

Files touched

Diff

commit fb9125e86965b875d878c2b8460087b8656a896b
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Thu May 14 01:06:03 2026 -0700

    fix(cli): emit structured JSON error from parents invoked without a subcommand in --agent mode (#1373)
    
    * fix(cli): emit structured JSON error from parents invoked without a subcommand in --agent / --json mode
    
    Generated parent commands (resource parents and the framework auth /
    profile / jobs / workflow / share groupings) had no RunE wired, so
    cobra fell through to its default help renderer. In --agent / --json
    mode that put human-readable help on stdout and exited 0 — agents
    piping the output through `jq` got a parse error with no signal that
    the invocation was incomplete.
    
    Adds a shared parentNoSubcommandRunE helper in helpers.go.tmpl that
    emits {"error":"subcommand required","valid_subcommands":[...]} on
    stdout and returns usageErr (exit 2) when flags.asJSON is set, falling
    back to cmd.Help() for human mode. Wired into command_parent.go.tmpl
    and the framework parents that shared the same shape.
    
    Human-mode behavior is unchanged: help still renders, exit 0.
    
    Closes #1138
    
    * test(cli): extend parent-RunE wiring test to cover workflow and share parents
---
 internal/generator/generator_test.go               | 63 ++++++++++++++++++++++
 internal/generator/templates/auth.go.tmpl          |  1 +
 internal/generator/templates/auth_browser.go.tmpl  |  1 +
 .../templates/auth_client_credentials.go.tmpl      |  1 +
 internal/generator/templates/auth_simple.go.tmpl   |  1 +
 .../generator/templates/channel_workflow.go.tmpl   |  1 +
 .../generator/templates/command_parent.go.tmpl     |  1 +
 internal/generator/templates/helpers.go.tmpl       | 26 +++++++++
 internal/generator/templates/jobs.go.tmpl          |  1 +
 internal/generator/templates/profile.go.tmpl       |  1 +
 .../generator/templates/share_commands.go.tmpl     |  1 +
 .../embedded-paged-api/internal/cli/helpers.go     | 26 +++++++++
 .../printing-press-oauth2-cc/internal/cli/auth.go  |  1 +
 .../printing-press-rich-auth/internal/cli/auth.go  |  1 +
 .../internal/cli/helpers.go                        | 26 +++++++++
 .../expected/generate-golden-api/dogfood.json      |  2 +-
 .../printing-press-golden/internal/cli/auth.go     |  1 +
 .../printing-press-golden/internal/cli/helpers.go  | 26 +++++++++
 .../printing-press-golden/internal/cli/profile.go  |  1 +
 .../internal/cli/projects_avatar.go                |  1 +
 20 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index afb3263d..ef4de667 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -9898,3 +9898,66 @@ func TestGenerateEndpointTemplateEnvOverridesWireThrough(t *testing.T) {
 	assert.Contains(t, string(readme), "ST_TENANT_ID",
 		"README must surface the override env var name in the runtime endpoint instructions")
 }
+
+// TestGenerateParentNoSubcommandRunE_WiredOnResourceParents: every generated
+// resource parent command must wire the parentNoSubcommandRunE helper so
+// invocations without a subcommand in --agent / --json mode get a structured
+// JSON error instead of human-readable cobra help on stdout. Before the fix,
+// agents driving the CLI silently received help text and exit 0. The helper
+// also has to be defined exactly once in helpers.go so the generated package
+// compiles. Multi-endpoint resources are exercised so a parent is actually
+// emitted (single-endpoint resources get promoted to a direct subcommand).
+func TestGenerateParentNoSubcommandRunE_WiredOnResourceParents(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("parent-runE-wired")
+	apiSpec.Resources = map[string]spec.Resource{
+		"items": {
+			Description: "Manage items",
+			Endpoints: map[string]spec.Endpoint{
+				"list": {Method: "GET", Path: "/items", Description: "List items"},
+				"get":  {Method: "GET", Path: "/items/{id}", Description: "Get one item"},
+			},
+		},
+	}
+	apiSpec.Share = spec.ShareConfig{Enabled: true, SnapshotTables: []string{"items"}}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	gen := New(apiSpec, outputDir)
+	gen.VisionSet = VisionTemplateSet{Store: true, Sync: true}
+	require.NoError(t, gen.Generate())
+
+	helpersSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "helpers.go"))
+	require.NoError(t, err)
+	assert.Contains(t, string(helpersSrc), "func parentNoSubcommandRunE(flags *rootFlags)",
+		"helpers.go must define parentNoSubcommandRunE so generated parents can wire it")
+	assert.Contains(t, string(helpersSrc), `"subcommand required"`,
+		"the helper must emit a structured JSON error envelope")
+	assert.Contains(t, string(helpersSrc), `"valid_subcommands"`,
+		"the JSON envelope must list valid subcommands so agents can self-correct")
+
+	parentSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "items.go"))
+	require.NoError(t, err)
+	assert.Regexp(t, `RunE:\s+parentNoSubcommandRunE\(flags\)`, string(parentSrc),
+		"the resource parent must call the shared helper instead of falling through to cobra's default help")
+
+	authSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "auth.go"))
+	require.NoError(t, err)
+	assert.Regexp(t, `RunE:\s+parentNoSubcommandRunE\(flags\)`, string(authSrc),
+		"the auth parent shares the same bug class and must wire the helper too")
+
+	profileSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "profile.go"))
+	require.NoError(t, err)
+	assert.Regexp(t, `RunE:\s+parentNoSubcommandRunE\(flags\)`, string(profileSrc),
+		"the profile parent shares the same bug class and must wire the helper too")
+
+	workflowSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "channel_workflow.go"))
+	require.NoError(t, err)
+	assert.Regexp(t, `RunE:\s+parentNoSubcommandRunE\(flags\)`, string(workflowSrc),
+		"the workflow parent shares the same bug class and must wire the helper too")
+
+	shareSrc, err := os.ReadFile(filepath.Join(outputDir, "internal", "cli", "share_commands.go"))
+	require.NoError(t, err)
+	assert.Regexp(t, `RunE:\s+parentNoSubcommandRunE\(flags\)`, string(shareSrc),
+		"the share parent shares the same bug class and must wire the helper too")
+}
diff --git a/internal/generator/templates/auth.go.tmpl b/internal/generator/templates/auth.go.tmpl
index 1565bfa1..1b05b7a2 100644
--- a/internal/generator/templates/auth.go.tmpl
+++ b/internal/generator/templates/auth.go.tmpl
@@ -29,6 +29,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: {{printf "%q" (authCommandShort .APISpec)}},
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthSetupCmd(flags))
diff --git a/internal/generator/templates/auth_browser.go.tmpl b/internal/generator/templates/auth_browser.go.tmpl
index a23b4f42..de2dda92 100644
--- a/internal/generator/templates/auth_browser.go.tmpl
+++ b/internal/generator/templates/auth_browser.go.tmpl
@@ -51,6 +51,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: {{printf "%q" (authCommandShort .APISpec)}},
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthLoginCmd(flags))
diff --git a/internal/generator/templates/auth_client_credentials.go.tmpl b/internal/generator/templates/auth_client_credentials.go.tmpl
index 1cfd7d01..14415de4 100644
--- a/internal/generator/templates/auth_client_credentials.go.tmpl
+++ b/internal/generator/templates/auth_client_credentials.go.tmpl
@@ -26,6 +26,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: {{printf "%q" (authCommandShort .APISpec)}},
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthLoginCmd(flags))
diff --git a/internal/generator/templates/auth_simple.go.tmpl b/internal/generator/templates/auth_simple.go.tmpl
index d6e0bc92..2370ee9c 100644
--- a/internal/generator/templates/auth_simple.go.tmpl
+++ b/internal/generator/templates/auth_simple.go.tmpl
@@ -24,6 +24,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: {{printf "%q" (authCommandShort .APISpec)}},
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthSetupCmd(flags))
diff --git a/internal/generator/templates/channel_workflow.go.tmpl b/internal/generator/templates/channel_workflow.go.tmpl
index 1c262a3a..27640132 100644
--- a/internal/generator/templates/channel_workflow.go.tmpl
+++ b/internal/generator/templates/channel_workflow.go.tmpl
@@ -25,6 +25,7 @@ func newWorkflowCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "workflow",
 		Short: "Compound workflows that combine multiple API operations",
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 {{- if .SyncEnabled}}
diff --git a/internal/generator/templates/command_parent.go.tmpl b/internal/generator/templates/command_parent.go.tmpl
index 198b884f..76d8dff8 100644
--- a/internal/generator/templates/command_parent.go.tmpl
+++ b/internal/generator/templates/command_parent.go.tmpl
@@ -14,6 +14,7 @@ func new{{camel .FuncPrefix}}Cmd(flags *rootFlags) *cobra.Command {
 {{- if .Hidden}}
 		Hidden: true,
 {{- end}}
+		RunE: parentNoSubcommandRunE(flags),
 	}
 {{range $eName, $endpoint := .Resource.Endpoints}}
 	cmd.AddCommand(new{{camel $.FuncPrefix}}{{camel $eName}}Cmd(flags))
diff --git a/internal/generator/templates/helpers.go.tmpl b/internal/generator/templates/helpers.go.tmpl
index 7d58d0d8..65074cf5 100644
--- a/internal/generator/templates/helpers.go.tmpl
+++ b/internal/generator/templates/helpers.go.tmpl
@@ -140,6 +140,32 @@ func dryRunOK(flags *rootFlags) bool {
 	return flags != nil && flags.dryRun
 }
 
+// parentNoSubcommandRunE returns a RunE that handles parents invoked without a
+// subcommand. In machine output (--json/--agent) the parent emits a structured
+// error to stdout listing valid subcommands and exits 2; otherwise cobra's
+// default help text is printed. Without this, agents driving the CLI in
+// --agent mode received only human-readable help on stdout and exit 0, with no
+// signal that the invocation was incomplete.
+func parentNoSubcommandRunE(flags *rootFlags) func(*cobra.Command, []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		if flags != nil && flags.asJSON {
+			subs := make([]string, 0, len(cmd.Commands()))
+			for _, c := range cmd.Commands() {
+				if c.IsAvailableCommand() && c.Name() != "help" {
+					subs = append(subs, c.Name())
+				}
+			}
+			sort.Strings(subs)
+			_ = json.NewEncoder(cmd.OutOrStdout()).Encode(map[string]any{
+				"error":             "subcommand required",
+				"valid_subcommands": subs,
+			})
+			return usageErr(fmt.Errorf("subcommand required for %q", cmd.CommandPath()))
+		}
+		return cmd.Help()
+	}
+}
+
 {{- if .HasDataLayer}}
 
 // accessWarning describes an API access-denial that sync converts into a
diff --git a/internal/generator/templates/jobs.go.tmpl b/internal/generator/templates/jobs.go.tmpl
index 9a95944b..de80b4d0 100644
--- a/internal/generator/templates/jobs.go.tmpl
+++ b/internal/generator/templates/jobs.go.tmpl
@@ -217,6 +217,7 @@ func newJobsCmd(flags *rootFlags) *cobra.Command {
 
 Submit an async endpoint with --wait to block until completion; submit
 without --wait to get the job ID back immediately and track it later.`,
+		RunE: parentNoSubcommandRunE(flags),
 	}
 	cmd.AddCommand(newJobsListCmd(flags))
 	cmd.AddCommand(newJobsGetCmd(flags))
diff --git a/internal/generator/templates/profile.go.tmpl b/internal/generator/templates/profile.go.tmpl
index 1296e472..513f1d49 100644
--- a/internal/generator/templates/profile.go.tmpl
+++ b/internal/generator/templates/profile.go.tmpl
@@ -153,6 +153,7 @@ agent can invoke the same command with the same configuration each run.
 
 Use --profile <name> on any command to apply that profile's values.
 Explicit flags override profile values.`,
+		RunE: parentNoSubcommandRunE(flags),
 	}
 	cmd.AddCommand(newProfileSaveCmd(flags))
 	cmd.AddCommand(newProfileUseCmd(flags))
diff --git a/internal/generator/templates/share_commands.go.tmpl b/internal/generator/templates/share_commands.go.tmpl
index 378a7274..ea3e12e1 100644
--- a/internal/generator/templates/share_commands.go.tmpl
+++ b/internal/generator/templates/share_commands.go.tmpl
@@ -27,6 +27,7 @@ One teammate with API credentials runs 'share publish' on a cadence; the
 rest run 'share subscribe <remote>' once, then read from the cache
 without needing credentials. Auto-refresh picks up new snapshots on the
 next read command.`,
+		RunE: parentNoSubcommandRunE(flags),
 	}
 	cmd.AddCommand(newShareExportCmd(flags))
 	cmd.AddCommand(newShareImportCmd(flags))
diff --git a/testdata/golden/expected/generate-embedded-paged-api/embedded-paged-api/internal/cli/helpers.go b/testdata/golden/expected/generate-embedded-paged-api/embedded-paged-api/internal/cli/helpers.go
index 146efdb7..73b178a8 100644
--- a/testdata/golden/expected/generate-embedded-paged-api/embedded-paged-api/internal/cli/helpers.go
+++ b/testdata/golden/expected/generate-embedded-paged-api/embedded-paged-api/internal/cli/helpers.go
@@ -124,6 +124,32 @@ func dryRunOK(flags *rootFlags) bool {
 	return flags != nil && flags.dryRun
 }
 
+// parentNoSubcommandRunE returns a RunE that handles parents invoked without a
+// subcommand. In machine output (--json/--agent) the parent emits a structured
+// error to stdout listing valid subcommands and exits 2; otherwise cobra's
+// default help text is printed. Without this, agents driving the CLI in
+// --agent mode received only human-readable help on stdout and exit 0, with no
+// signal that the invocation was incomplete.
+func parentNoSubcommandRunE(flags *rootFlags) func(*cobra.Command, []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		if flags != nil && flags.asJSON {
+			subs := make([]string, 0, len(cmd.Commands()))
+			for _, c := range cmd.Commands() {
+				if c.IsAvailableCommand() && c.Name() != "help" {
+					subs = append(subs, c.Name())
+				}
+			}
+			sort.Strings(subs)
+			_ = json.NewEncoder(cmd.OutOrStdout()).Encode(map[string]any{
+				"error":             "subcommand required",
+				"valid_subcommands": subs,
+			})
+			return usageErr(fmt.Errorf("subcommand required for %q", cmd.CommandPath()))
+		}
+		return cmd.Help()
+	}
+}
+
 // accessWarning describes an API access-denial that sync converts into a
 // non-fatal warning. It carries enough structured data for the sync_warning
 // JSON event without parsing free-form error strings downstream.
diff --git a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/cli/auth.go b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/cli/auth.go
index 84e90080..787745a0 100644
--- a/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/cli/auth.go
+++ b/testdata/golden/expected/generate-golden-api-oauth2-cc/printing-press-oauth2-cc/internal/cli/auth.go
@@ -26,6 +26,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: "Manage authentication for Printing Press Oauth2",
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthLoginCmd(flags))
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/auth.go b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/auth.go
index 08fb4068..a9f3791f 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/auth.go
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/auth.go
@@ -14,6 +14,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: "Manage authentication for Printing Press Rich",
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthSetupCmd(flags))
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/helpers.go b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/helpers.go
index f6db72c0..33ef4ad3 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/helpers.go
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/internal/cli/helpers.go
@@ -123,6 +123,32 @@ func dryRunOK(flags *rootFlags) bool {
 	return flags != nil && flags.dryRun
 }
 
+// parentNoSubcommandRunE returns a RunE that handles parents invoked without a
+// subcommand. In machine output (--json/--agent) the parent emits a structured
+// error to stdout listing valid subcommands and exits 2; otherwise cobra's
+// default help text is printed. Without this, agents driving the CLI in
+// --agent mode received only human-readable help on stdout and exit 0, with no
+// signal that the invocation was incomplete.
+func parentNoSubcommandRunE(flags *rootFlags) func(*cobra.Command, []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		if flags != nil && flags.asJSON {
+			subs := make([]string, 0, len(cmd.Commands()))
+			for _, c := range cmd.Commands() {
+				if c.IsAvailableCommand() && c.Name() != "help" {
+					subs = append(subs, c.Name())
+				}
+			}
+			sort.Strings(subs)
+			_ = json.NewEncoder(cmd.OutOrStdout()).Encode(map[string]any{
+				"error":             "subcommand required",
+				"valid_subcommands": subs,
+			})
+			return usageErr(fmt.Errorf("subcommand required for %q", cmd.CommandPath()))
+		}
+		return cmd.Help()
+	}
+}
+
 // accessWarning describes an API access-denial that sync converts into a
 // non-fatal warning. It carries enough structured data for the sync_warning
 // JSON event without parsing free-form error strings downstream.
diff --git a/testdata/golden/expected/generate-golden-api/dogfood.json b/testdata/golden/expected/generate-golden-api/dogfood.json
index f755805e..697673ba 100644
--- a/testdata/golden/expected/generate-golden-api/dogfood.json
+++ b/testdata/golden/expected/generate-golden-api/dogfood.json
@@ -16,7 +16,7 @@
   },
   "dead_functions": {
     "dead": 0,
-    "total": 58
+    "total": 59
   },
   "dir": "<ARTIFACT_DIR>/generate-golden-api/printing-press-golden",
   "example_check": {
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/auth.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/auth.go
index a9c90486..d6dfc242 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/auth.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/auth.go
@@ -14,6 +14,7 @@ func newAuthCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "auth",
 		Short: "Manage authentication for Printing Press Studio",
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newAuthSetupCmd(flags))
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/helpers.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/helpers.go
index feba1e1e..3dcd6f7b 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/helpers.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/helpers.go
@@ -124,6 +124,32 @@ func dryRunOK(flags *rootFlags) bool {
 	return flags != nil && flags.dryRun
 }
 
+// parentNoSubcommandRunE returns a RunE that handles parents invoked without a
+// subcommand. In machine output (--json/--agent) the parent emits a structured
+// error to stdout listing valid subcommands and exits 2; otherwise cobra's
+// default help text is printed. Without this, agents driving the CLI in
+// --agent mode received only human-readable help on stdout and exit 0, with no
+// signal that the invocation was incomplete.
+func parentNoSubcommandRunE(flags *rootFlags) func(*cobra.Command, []string) error {
+	return func(cmd *cobra.Command, args []string) error {
+		if flags != nil && flags.asJSON {
+			subs := make([]string, 0, len(cmd.Commands()))
+			for _, c := range cmd.Commands() {
+				if c.IsAvailableCommand() && c.Name() != "help" {
+					subs = append(subs, c.Name())
+				}
+			}
+			sort.Strings(subs)
+			_ = json.NewEncoder(cmd.OutOrStdout()).Encode(map[string]any{
+				"error":             "subcommand required",
+				"valid_subcommands": subs,
+			})
+			return usageErr(fmt.Errorf("subcommand required for %q", cmd.CommandPath()))
+		}
+		return cmd.Help()
+	}
+}
+
 // accessWarning describes an API access-denial that sync converts into a
 // non-fatal warning. It carries enough structured data for the sync_warning
 // JSON event without parsing free-form error strings downstream.
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/profile.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/profile.go
index f3b60421..66d695b4 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/profile.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/profile.go
@@ -153,6 +153,7 @@ agent can invoke the same command with the same configuration each run.
 
 Use --profile <name> on any command to apply that profile's values.
 Explicit flags override profile values.`,
+		RunE: parentNoSubcommandRunE(flags),
 	}
 	cmd.AddCommand(newProfileSaveCmd(flags))
 	cmd.AddCommand(newProfileUseCmd(flags))
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar.go b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar.go
index bb6503f1..01f7bd3f 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar.go
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/internal/cli/projects_avatar.go
@@ -11,6 +11,7 @@ func newProjectsAvatarCmd(flags *rootFlags) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:   "avatar",
 		Short: "Manage avatar",
+		RunE:  parentNoSubcommandRunE(flags),
 	}
 
 	cmd.AddCommand(newProjectsAvatarUploadProjectCmd(flags))

← d44587e9 test(cli): regression tests for body-flag identifier collisi  ·  back to Cli Printing Press  ·  fix(cli): drop shell line comments + skip happy_path on miss 295fd03f →