Returning multiple errors from a Go function

A convenient way of returning multiple error values from a single function is to write a custom MultiError type that itself imlements the error interface.

type MultiError struct {
        errs []error
}

func (m MultiError) Error() string {
        switch len(m.errs) {
                case 0:
                return "(0 errors)"
        case 1:
                return m.errs[0].Error()
        case 2:
                return m.errs[0].Error() + " (and 1 other error)"
        default:
                return fmt.Sprintf("%s (and %d other errors)",
                        m.errs[0].Error(), len(m.errs)-1)
        }
}

We also need a way of setting errors.

// Adds one or many errors.
func (m *MultiError) Add(es ...error) {
        for _, e := range es {
                m.errs = append(m.errs, e)
        }
}

We could expose the number of errors set but we don’t want to manually check if we have to return our type as error anyway. That’s why we implement a function Return() that returns either nil or a pointer to the MultiError instance itself. It can be called directly in the calling function’s return statement.

// Returns itself if errors are set, otherwise nil.
func (m *MultiError) Return() error {
        if len(m.errs) > 0 {
                return m
        } else {
                return nil
        }
}

It is very important to let this function return the interface type error and not the concrete one MultiError so the caller is able to compare with nil. If you don’t know why read Why is my nil error value not equal to nil? in the Go FAQ.

This type could easily be extended with an Append(ms ...MultiError) function or an Each(f func(error)) for logging all set errors in a decent way.

Example usage

func sendTenRequests() error {
        mErr := MultiError{}
        mErr.Add(fmt.Errorf("first request failed"))
        mErr.Add(fmt.Errorf("second request failed"))
        mErr.Add(fmt.Errorf("another failed one"))
        return mErr.Return()
}

[...]

err := sendTenRequests()
if err != nil {
	fmt.Fprintf(os.Stderr, "%s: %s\n", os.Args[0], err)
	return 1
}
First published on November 30, 2015