Cobra: Docs needed: how best to handle errors in Run

Created on 20 Jul 2019  路  4Comments  路  Source: spf13/cobra

Reading the documentation on the front page, it is not clear to me what the best practice for raising errors from a command's Run func is?

Looking at usage of cobra in other projects, the way seems to be just to return os.Exit(1) from the body of the Run func. I'm a total Go noob so my instinct is probably wrong, but this didn't feel quite 'right' to me. Wouldn't that bypass any PostRun hooks? They feel like lifecycle events that should always get a chance to run though.

Then I read to the bottom of issue https://github.com/spf13/cobra/issues/67 and I can see some code got merged four years ago adding a RunE hook which returns error. (Forgive me if I missed it but it seems this is not mentioned in the docs anywhere? https://github.com/spf13/cobra#prerun-and-postrun-hooks Are they deprecated?)

This seemed like what I wanted, but the downside is this causes the 'usage' help text to be output. But that's not always appropriate - maybe the user gave perfectly good args to the command but somewhere in the processing of the job an error had to be returned.

So I have gone back to calling os.Exit from the body of Run. In the end this is fine for my use case.

All this is just to say it would be good to have the framework's opinion on how to handle errors expressed in the docs somewhere.

kinstale

Most helpful comment

After reading the Command code for a while, I don't see a really flexible way to decide how to handle errors at runtime. This is the approach we've ended up on:

func main() {
    if err := command.RootCmd.Execute(); err != nil {
        if err != command.SilentErr {
            fmt.Fprintln(os.Stderr, err)
        }
        os.Exit(1)
    }
}
package command

var SilentErr = errors.New("SilentErr")

var RootCmd = &cobra.Command{
    // ...
    SilenceErrors: true,
    SilenceUsage:  true,
}

func init() {
    RootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
        cmd.Println(err)
        cmd.Println(cmd.UsageString())
        return SilentErr
    })
}

After this, we use RunE for all commands.

The features of this approach are:

  • errors in flag parsing are printed together with command usage help;
  • all other errors are handled in main().

Drawbacks:

  • doesn't print command usage help for unknown command "SUBCMD" for "CMD" errors.

Ref. https://github.com/spf13/cobra/issues/340

All 4 comments

To suppress the usage, use the SilenceUsage field.

However, I totally agree with you on that it needs some kind of documentation.
Currently wondering why PersistentPostRuns are not executed when RunE returns an error. This is, in my opinion, not obvious. And I would like to still run the PersistentPostRun, instead of just exiting.

Any update on this topic please ?

I am also looking at good practices about errors handling in Cobra. The RunE seems to be really good, but the fact that there is nothing on the documentation is confusing, like if it was not good to use it.

After reading the Command code for a while, I don't see a really flexible way to decide how to handle errors at runtime. This is the approach we've ended up on:

func main() {
    if err := command.RootCmd.Execute(); err != nil {
        if err != command.SilentErr {
            fmt.Fprintln(os.Stderr, err)
        }
        os.Exit(1)
    }
}
package command

var SilentErr = errors.New("SilentErr")

var RootCmd = &cobra.Command{
    // ...
    SilenceErrors: true,
    SilenceUsage:  true,
}

func init() {
    RootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
        cmd.Println(err)
        cmd.Println(cmd.UsageString())
        return SilentErr
    })
}

After this, we use RunE for all commands.

The features of this approach are:

  • errors in flag parsing are printed together with command usage help;
  • all other errors are handled in main().

Drawbacks:

  • doesn't print command usage help for unknown command "SUBCMD" for "CMD" errors.

Ref. https://github.com/spf13/cobra/issues/340

This issue is being marked as stale due to a long period of inactivity

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eine picture eine  路  5Comments

alapidas picture alapidas  路  3Comments

groenborg picture groenborg  路  5Comments

joernott picture joernott  路  6Comments

lukasmalkmus picture lukasmalkmus  路  4Comments