diff --git a/cmd/root.go b/cmd/root.go index eebbfa67..425bded7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -391,8 +391,18 @@ func unknownSubcommandRunE(cmd *cobra.Command, args []string) error { Message: fmt.Sprintf("unknown flag %s before a subcommand for %q", strings.Join(unknown, ", "), cmd.CommandPath()), Hint: fmt.Sprintf("flags belong to a subcommand; run `%s --help` to list subcommands and their flags", cmd.CommandPath()), Detail: map[string]any{ + // Keep the same detail keys as flagDidYouMean's unknown_flag + // so a consumer keyed on Type can read a stable shape. The + // subcommand isn't resolved here, so suggestions/valid_flags + // have no meaningful universe to draw from — emit empty + // rather than the group's own (misleading) flags. unknown is + // the back-compat singular field; unknown_flags carries the + // full list when more than one flag was supplied. + "unknown": strings.Join(unknown, ", "), "unknown_flags": unknown, "command_path": cmd.CommandPath(), + "suggestions": []string{}, + "valid_flags": []string{}, }, }, } diff --git a/cmd/unknown_subcommand_test.go b/cmd/unknown_subcommand_test.go index 7f68fdaa..e765341e 100644 --- a/cmd/unknown_subcommand_test.go +++ b/cmd/unknown_subcommand_test.go @@ -125,6 +125,31 @@ func TestUnknownSubcommandRunE_FlagBeforeSubcommandIsStructured(t *testing.T) { if !strings.Contains(err.Error(), "unknown flag") { t.Errorf("error = %q, want it to mention an unknown flag", err.Error()) } + + // The detail must stay schema-compatible with flagDidYouMean's unknown_flag + // (same Type → same keys), so a consumer keyed on Type reads a stable shape. + exitErr, ok := err.(*output.ExitError) + if !ok || exitErr.Detail == nil { + t.Fatalf("expected *output.ExitError with Detail, got %T", err) + } + if exitErr.Detail.Type != "unknown_flag" { + t.Errorf("detail.Type = %q, want unknown_flag", exitErr.Detail.Type) + } + detail, ok := exitErr.Detail.Detail.(map[string]any) + if !ok { + t.Fatalf("expected detail to be map[string]any, got %T", exitErr.Detail.Detail) + } + if detail["unknown"] != "--badflag" { + t.Errorf("detail.unknown = %v, want --badflag", detail["unknown"]) + } + if got, _ := detail["unknown_flags"].([]string); len(got) != 1 || got[0] != "--badflag" { + t.Errorf("detail.unknown_flags = %v, want [--badflag]", detail["unknown_flags"]) + } + for _, key := range []string{"suggestions", "valid_flags"} { + if _, present := detail[key]; !present { + t.Errorf("detail.%s missing; must be present (empty) to match the unknown_flag schema", key) + } + } } func TestUnknownSubcommandRunE_NoArgsShowsHelp(t *testing.T) {