cobra
cobra copied to clipboard
Docs needed: how best to handle errors in Run
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.
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 PersistentPostRun
s 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
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
PersistentPostRun
s are not executed whenRunE
returns an error. This is, in my opinion, not obvious. And I would like to still run thePersistentPostRun
, instead of just exiting.
I'd also like to run a PersistentPostRun
hook independent of the return (error or success). I didn't find any mention of this in the documentation either.
Reading the docs, I thought RunE would set the error code on exit for me. I did not expect it to print usage info.
It is hilarious to have my service exit with an error (because of misconfig), and then, rather than print that error, it prints the usage info.
Different implementations may differ about exit codes to use, presumably thats why its left out of the implementation. Just as easy as it would be to configure a field ExitCodeOnError=1
(just for instance) it is as easy to do an os.Exit(1)
as well. That's my take on why its remained that way.
I agree that it wouldn't be obvious that the postRun methods wouldn't be run if an error occurs. That does seem like something that would be useful to be configurable. You'd wan't the error from the RunE
to bubble to the next phase but we wouldn't want to change any of the method signatures. I guess the only implementation would be to add another field to the *cobra.Command object that holds any error encountered during the execution.
I've seen that pattern used before; e.g.
type Foo struct{
err error
}
func (f *Foo) doA(){
if f.err != nil {
return // custom behavior if errors have occurred in the past
}
doA() // actually continue
Thoughts on that approach?
I wanted to bring this up once again to see if anyone had made any progress or had thoughts on how to run PersistentPostRun
after a run, despite any errors.
Currently, in both the Pre
and Post
runs, I have some code running that takes care of some data collection related operations. Because of this, even after errors, I want to make sure that despite what command was run, and whether it succeeded or failed, I want to make sure the application properly cleans up anything that it was doing. These operations are the same for every single subcommand of the root, so I thought that using Pre
and Post
hooks would be the correct place to define them.
Did anyone succeed in finding workarounds for this? Because I have so many subcommands, it seems like bad design to have each of the subcommands contain some logic to perform this operation.