cobra: Return "unknown command" error for unknown subcommands.

Right now there is a difference in handling between unknown command and unknown subcommand. Let’s consider the example:

package main

import "github.com/spf13/cobra"

func main() {

	subCmd := &cobra.Command{
		Use: "foo",
	}
	subCmd.AddCommand(&cobra.Command{
		Use: "bar",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Println("bar")
		},
	})
	subCmd.AddCommand(&cobra.Command{
		Use: "baz",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Println("baz")
		},
	})

	rootCmd := &cobra.Command{
		Use: "test",
	}
	rootCmd.AddCommand(subCmd)
	rootCmd.Execute()
}

When I run it as ./test unknown I get ‘unknown command’ error:

Error: unknown command "unknown" for "test"
Run 'test --help' for usage.

If I run it as ./test foo unknown I receive help message:

$ go run main.go foo unknown
Usage:
  test foo [command]

Available Commands:
  bar         
  baz         

Flags:
  -h, --help   help for foo

Use "test foo [command] --help" for more information about a command.

It seems very much like inconsistency.

I suppose that we don’t need to return flag.ErrHelp on not runnable command and treat it like “unknown subcommand”.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 14
  • Comments: 17

Commits related to this issue

Most upvoted comments

instead of setting, PositionalArgs: MaximumNArgs(0), you can set Args: cobra.NoArgs, the result of which is that the subcommand’s ‘unknown command’ behavior will be consistent with that of the top-level command. Run needs to be set to some dummy function. e.g.:

var someCmd = &cobra.Command{
	Use:   "some",
	Args: cobra.NoArgs,
	Run:  func(*cobra.Command, []string) {},
}

Since this issue had quite a bit of discussion and reaction I’m removing the stale label and marking as needing more triage. I’m a new maintainer so I am unsure if this had been discussed more out of band from this issue. The fact that the first comment mentioned it was a known/accepted state of things makes me unsure if there is intent to fix it.

Since numerous users are asking for it, it seems very reasonable though.

This is just broken - please fix

Unfortunately not, referring to your example Original returning an error (by cobra), the usage but exit 0 With Run we can return an error (by ourselves), optionally the usage and Exit 1, but as you can see, the Usage line is double (what I’m referring to in my last comment) - Which is technically the truth, since we’ve provided a Run() function! (It can be run by itself with persistentFlags OR using a subcommand which will have its own flags) With Run + SilenceUsage everywhere no change in the effective Usage() output

That’s why - since we don’t want to change break the current behaviour, I’m suggesting that we have a way for defining, that example “Original” exits with 1 - without all these hacky workarounds in the following examples, which lead to technically correct behaviour!

That means, my suggestion with cobra.NoFunc is actually not a good idea, better would be rootCmd = cobra.Command{..., ExitErrorOnMissingSubcommand: true, ...} or something like that

Or actually changing the behaviour and exit 1 on usage error always and note that as breaking change for a new major release branch

While writing this up, checking some of the current tooling for their behaviour, I realized

  • git has only 1 command level (e.g. git log)
  • ip has 2 levels, but if only provided the first level (e.g. ip netns), it automatically means list of the 2nd level
  • kubectl errors 1 if there are missing subcommands (e.g. kubectl get), but they have overridden anyway all possible aspects of cobra
  • lxc errors 0 (e.g. lxc profile), but they also use cobra so “suffering” from the exactly this topic here
  • openstack python client errors 2 (e.g. openstack server) - the first real-world example I’ve found without go or cobra