urfave/cli · info · exitError

%+v

Error message

%+v

What it means

cli.Exit builds an ExitCoder from a message and code. When the message is neither an error nor an ErrorFormatter, it is formatted with fmt.Errorf("%+v", message), so the resulting error's text is the %+v rendering of whatever was passed (e.g. a struct dump).

Source

Thrown at errors.go:134

type exitError struct {
	exitCode int
	err      error
}

// Exit wraps a message and exit code into an error, which by default is
// handled with a call to os.Exit during default error handling.
//
// This is the simplest way to trigger a non-zero exit code for a Command without
// having to call os.Exit manually. During testing, this behavior can be avoided
// by overriding the ExitErrHandler function on a Command or the package-global
// OsExiter function.
func Exit(message any, exitCode int) ExitCoder {
	var err error

	switch e := message.(type) {
	case ErrorFormatter:
		err = fmt.Errorf("%+v", message)
	case error:
		err = e
	default:
		err = fmt.Errorf("%+v", message)
	}

	return &exitError{
		err:      err,
		exitCode: exitCode,
	}
}

func (ee *exitError) Error() string {
	return ee.err.Error()
}

func (ee *exitError) ExitCode() int {
	return ee.exitCode

View on GitHub (pinned to 1a4deb4f5a)

Solutions

  1. Pass a string or errors.New(...) as the message
  2. If passing structured data, format it first with fmt.Sprintf("%v", data)
  3. Implement the ErrorFormatter interface so %+v yields a readable message

Example fix

// before
return cli.Exit(myStruct, 1)
// after
return cli.Exit(fmt.Sprintf("invalid input: %v", myStruct), 1)
Defensive patterns

Strategy: type-guard

Type guard

func exitMsg(v any) error {
    switch m := v.(type) {
    case error:
        return m
    case fmt.Stringer:
        return errors.New(m.String())
    default:
        return errors.New(fmt.Sprint(v))
    }
}

Try / catch

err := cli.Exit(msg, 1)
// inspect what will be printed before exiting
if !isUserReadable(fmt.Sprint(msg)) { msg = fmt.Sprintf("exit: %v", msg) }

Prevention

When it happens

Trigger: Calling cli.Exit with a non-error, non-ErrorFormatter value such as a struct, slice, or map; the message text is then '%+v' output rather than a human-readable string.

Common situations: Passing raw data structures to Exit instead of formatting them first; assuming Exit takes a string; refactors that change a string argument to a typed value.

Related errors


AI-assisted analysis of urfave/cli@1a4deb4f5a (2026-08-31). Data as JSON: /api/errors/c4b1149e698bd873. Report an issue: GitHub.