Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs needed: how best to handle errors in Run #914

Open
anentropic opened this issue Jul 19, 2019 · 10 comments
Open

Docs needed: how best to handle errors in Run #914

anentropic opened this issue Jul 19, 2019 · 10 comments
Labels
kind/documentation Documentation of cobra itself kind/support Questions, supporting users, etc.

Comments

@anentropic
Copy link

anentropic commented Jul 19, 2019

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 #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.

@tommyknows
Copy link

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.

@fallais
Copy link

fallais commented Sep 30, 2019

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.

@mislav
Copy link

mislav commented Oct 31, 2019

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. #340

@github-actions
Copy link

github-actions bot commented Apr 5, 2020

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

bfirsh added a commit to replicate/keepsake that referenced this issue Sep 30, 2020
Without SilenceUsage, errors in RunE also produce usage, so we have
to make our own wrapper that prints errors without usage.

See also:
spf13/cobra#340
spf13/cobra#914

Closes #159
bfirsh added a commit to replicate/keepsake that referenced this issue Oct 1, 2020
Without SilenceUsage, errors in RunE also produce usage, so we have
to make our own wrapper that prints errors without usage.

See also:
spf13/cobra#340
spf13/cobra#914

Closes #159
denopink added a commit to denopink/subo that referenced this issue Nov 23, 2021
…Cmd failures

Based on the conv in suborbital#119,  we want to delete the output dir of `sudo create runnable` for any failure that occurs in `CreateRunnableCmd`.

`CreateRunnableError`s `Error` will also act as a cleanup function for these failures. atm, it's only deleting the output dir.

Ideally, cobra.Command would have a error callback feature... maybe we'll get one for Xmas. spf13/cobra#914
@jeanmorais
Copy link

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.

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.

@johnSchnake johnSchnake added kind/documentation Documentation of cobra itself kind/support Questions, supporting users, etc. labels Feb 21, 2022
@AWinterman
Copy link

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.

@johnSchnake
Copy link
Collaborator

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?

@sacheth003
Copy link

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.

vrothberg added a commit to vrothberg/libpod that referenced this issue Jan 10, 2023
If the run errors, cobra does not execute post runs.  It is a somehow
known issue (spf13/cobra#914) but problematic
for Podmand as the runtime is shutdown during post run.

Since some commands overwrite the post run and a general lack in cobra
of post runs on errors, move the shutting down the engines directly into
Execute.  Fixing the issue may fix a number of flakes.

Note that the shutdowns are NOPs for the remote client.

Signed-off-by: Valentin Rothberg <[email protected]>
@benoittgt
Copy link

I love the suggestion by @mislav on #914 (comment) but when you need to test the command with error having an os.exit(1) it's not the easiest to handle.

@n0rad
Copy link

n0rad commented Jul 10, 2024

If you don't need the command, you could just use cobra.OnFinalize().

If you need the command like me to prepare metrics of usage, you could store the in flight command and do process after the execute:

var inFlightCommand *cobra.Command
...

		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			inFlightCommand = cmd
                },

err := command.RootCmd.Execute();
// Do something using the `err` containing the result and the `inFlightCommand` that point to the command

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/documentation Documentation of cobra itself kind/support Questions, supporting users, etc.
Projects
None yet
Development

No branches or pull requests

10 participants