A Go (golang) package for representing a list of errors as a single error.

go-multierror

Build Status Go Documentation

go-multierror is a package for Go that provides a mechanism for representing a list of error values as a single error.

This allows a function in Go to return an error that might actually be a list of errors. If the caller knows this, they can unwrap the list and access the errors. If the caller doesn't know, the error formats to a nice human-readable format.

go-multierror is fully compatible with the Go standard library errors package, including the functions As, Is, and Unwrap. This provides a standardized approach for introspecting on error values.

Installation and Docs

Install using go get github.com/hashicorp/go-multierror.

Full documentation is available at http://godoc.org/github.com/hashicorp/go-multierror

Usage

go-multierror is easy to use and purposely built to be unobtrusive in existing Go applications/libraries that may not be aware of it.

Building a list of errors

The Append function is used to create a list of errors. This function behaves a lot like the Go built-in append function: it doesn't matter if the first argument is nil, a multierror.Error, or any other error, the function behaves as you would expect.

var result error

if err := step1(); err != nil {
	result = multierror.Append(result, err)
}
if err := step2(); err != nil {
	result = multierror.Append(result, err)
}

return result

Customizing the formatting of the errors

By specifying a custom ErrorFormat, you can customize the format of the Error() string function:

var result *multierror.Error

// ... accumulate errors here, maybe using Append

if result != nil {
	result.ErrorFormat = func([]error) string {
		return "errors!"
	}
}

Accessing the list of errors

multierror.Error implements error so if the caller doesn't know about multierror, it will work just fine. But if you're aware a multierror might be returned, you can use type switches to access the list of errors:

if err := something(); err != nil {
	if merr, ok := err.(*multierror.Error); ok {
		// Use merr.Errors
	}
}

You can also use the standard errors.Unwrap function. This will continue to unwrap into subsequent errors until none exist.

Extracting an error

The standard library errors.As function can be used directly with a multierror to extract a specific error:

// Assume err is a multierror value
err := somefunc()

// We want to know if "err" has a "RichErrorType" in it and extract it.
var errRich RichErrorType
if errors.As(err, &errRich) {
	// It has it, and now errRich is populated.
}

Checking for an exact error value

Some errors are returned as exact errors such as the ErrNotExist error in the os package. You can check if this error is present by using the standard errors.Is function.

// Assume err is a multierror value
err := somefunc()
if errors.Is(err, os.ErrNotExist) {
	// err contains os.ErrNotExist
}

Returning a multierror only if there are errors

If you build a multierror.Error, you can use the ErrorOrNil function to return an error implementation only if there are errors to return:

var result *multierror.Error

// ... accumulate errors here

// Return the `error` only if errors were added to the multierror, otherwise
// return nil since there are no errors.
return result.ErrorOrNil()
Owner
HashiCorp
Consistent workflows to provision, secure, connect, and run any infrastructure for any application.
HashiCorp
Comments
  • Fix travis setup to work with go modules

    Fix travis setup to work with go modules

    Just to get the build passing again!

    I followed: https://dave.cheney.net/2018/07/16/using-go-modules-with-travis-ci and: https://arslan.io/2018/08/26/using-go-modules-with-vendor-support-on-travis-ci/

  • Update error output to be more readable

    Update error output to be more readable

    • Don't space out the error but append the next one
    • During a test, type the expected result instead of parsing it

    Current error output looks like:

    Error applying plan:
    
    3 error(s) occurred:
    
    * azurerm_network_interface.machine1: 1 error(s) occurred:
    
    * azurerm_network_interface.machine1: <some error string goes here>
    * azurerm_network_interface.machine1: <some error string goes here>
    * azurerm_network_interface.machine2: 1 error(s) occurred:
    
    * azurerm_network_interface.machine: <some error string goes here>
    

    This changes it to be:

    Error applying plan:
    
    3 error(s) occurred:
    * azurerm_network_interface.machine1: 2 error(s) occurred:
    	* azurerm_network_interface.machine1: <some error string goes here>
    	* azurerm_network_interface.machine1: <some error string goes here>
    
    * azurerm_network_interface.machine2: 1 error(s) occurred:
    	* azurerm_network_interface.machine2: <some error string goes here>
    
    

    Which imho makes things more readable.

  • Appending nil values results in a non-nil error

    Appending nil values results in a non-nil error

    Loving this library. One thing surprised me though:

    var err error
    var err2 error
    err = multierror.Append(err, err2)
    // err.Error() "0 errors occurred:"
    

    Practically speaking, instead of doing this:

    var err error
    for _, chrome := range m.chromes {
    	err = multierror.Append(err, chrome.Stop())
    }
    return err
    

    You have to do this:

    var err error
    for _, chrome := range m.chromes {
    	e := chrome.Stop()
    	if e != nil {
    		err = multierror.Append(err, e)
    	}
    }
    return err
    

    Not terrible, just a bit unexpected. Would you accept a PR fixing this? Thanks!

  • Go version missing from documentations.

    Go version missing from documentations.

    I'm using golang v1.12 but this package is not supported because of errors.Is and errors.As functions which are not present in v1.12 and was added in v1.13. And there is no version requirement mentioned in the documentation. for this package

  • Return the stdlib error interface

    Return the stdlib error interface

    Thanks for an excellent library!

    I'm opening this pull request to propose a several very small changes which are intended to make the Append function "behave as you would expect" (as is suggested in the README).

    Changes:

    • Return the standard library error interface from Append (this is a change to the function signature)

      Note the following advice from the golang FAQ

      It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.

    • Return the standard library error interface from (g *Group) Wait() (this is a change to the function signature)

    • Return nil from Append when there is no error

    • Return the original error from Append when there is only one error (i.e. appending to nil)

    • Introduce a new Cast method to allow older code which requires a *multierror.Error to be easily updated

    • Change the Group.err field from *Error to error

  • Build error triggered from the mxpv/podsync makefile

    Build error triggered from the mxpv/podsync makefile

    I am trying to build mxpv/podsync using its makefile, and I am getting this error:

    # github.com/hashicorp/go-multierror ../go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As ../go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is

    I know basically nothing about go, but as far as I can tell this shouldn't be happening. Is this a bug in the podsync makefile, or is it a bug in multierror.go?

    I would appreciate the attention of someone who understands these things better than I. Thanks!

    (For further context, see my issue on the podsync repository here.)

  • Support Go 1.13 errors.As/Is/Unwrap functionality

    Support Go 1.13 errors.As/Is/Unwrap functionality

    The primary mechanism that enables this functionality is making Unwrap on the top-level Error return a new "chain" structure that uses state to keep track of the current error.

    The chain implements errors.Is/As so that it compares to that current underlying error. And it implements Unwrap to move on to the next error.

    A well-formed program using errors.Is/As/Unwrap exclusively will behave correctly with go-multierror in this case without dropping any errors. Direct comparisons such as Unwrap() == myErr will not work because we wrap in a chain. The user has to do errors.Is(err, myErr) which is the right thing to do anyways.

    When Unwrap is called on a top-level *Error, we create a shallow copy of the errors so that you can continue using *Error (modifying it, potentially in-place). There is a slight cost to this but it felt weird that calling Unwrap would share the same underlying data and cause potential data races. I think this is unlikely, but its also very unlikely the performance cost of the shallow copy of errors will matter either.

  • fix subtle interface bug on Group.Wait()

    fix subtle interface bug on Group.Wait()

    Returning the concrete type *Error causes any assignments to an error type to result in a non-nil value, since the error interface would actually have (*Error, nil) rather than (nil, nil). This matches the sync/errgroup interface.

    cc: @nickethier

  • Marshalling multierror

    Marshalling multierror

    Hey,

    I am using your multierror package in my project however I need to wrap it and add a MarshalJSON as it doesn't Marshal out of the box (because of the Func field). Would it be ok to add it to your project? In my case it would just marshal the error array. I don't know if you would want to do something more elaborate

  • Check if multierror is nil in WrappedErrors

    Check if multierror is nil in WrappedErrors

    This adds a check similar to ErrorsOrNil that ensures the multierror pointer is not nil before returning the Errors field. I believe this is fully backwards-compatible, because the former behavior is a runtime panic.

  • A new release with Group

    A new release with Group

    Can you make a new release, including Group in order to make it available in go modules? It is already present in your documentation, so I think, that it is reasonable to include it in a new release.

  • Please change license

    Please change license

    Hello, thanks for a useful library. Am I correct that it doesn't appear to provide unique competitive advantage to Hashicorp?

    Could it (and hashicorp/errwrap) be changed to a permissive license? That would resolve the only persistent error in our binary scanners... Thanks for your consideration!

  • [COMPLIANCE] Update MPL-2.0 LICENSE

    [COMPLIANCE] Update MPL-2.0 LICENSE

    Hi there 👋

    This PR was auto-generated as part of an internal review of public repositories that are not in compliance with HashiCorp's licensing standards.

    Frequently Asked Questions

    Why am I getting this PR? This pull request was created because one or more of the following criteria was found:
    • This repo did not previously have a LICENSE file
    • A LICENSE file was present, but had a non-conforming name (e.g., license.txt)
    • A LICENSE file was present, but was missing an appropriate copyright statement

    More info is available in the RFC

    How do you determine the copyright date? The copyright date given in this PR is supposed to be the year the repository or project was created (whichever is older). If you believe the copyright date given in this PR is not valid, please reach out to:

    #proj-software-copyright

    I don't think this repo should be licensed under the terms of the Mozilla Public License 2.0. Who should I reach out to? If you believe this repository should not use an MPL 2.0 License, please reach out to [email protected]. Exemptions are considered on a case-by-case basis, but common reasons include if the project is co-managed by another entity that requires differing license terms, or if the project is part of an ecosystem that commonly uses a different license type (e.g., MIT or Apache 2.0).

    Please approve and merge this PR in a timely manner to keep this source code compliant with our OSS license agreement. If you have any questions or feedback, reach out to #proj-software-copyright.

    Thank you!


    Made with :heart: @HashiCorp

  • Invitation to comment on proposed standard library multierror support

    Invitation to comment on proposed standard library multierror support

    Hi!

    I have an in-review proposal to add support for wrapping multiple errors to the standard library. The latest version of the proposal is: https://github.com/golang/go/issues/53435#issuecomment-1191752789

    This proposal is intended to allow interoperation between multierror implementations, as well as to simplify these implementations. It isn't intended to supplant existing implementations, except in the simplest of cases. The quick summary of the proposal is that errors.Is and errors.As would support descending into multiple errors when a type implements an Unwrap() []error method returning a list of wrapped errors; for details see the above link.

    I'm opening an issue in this repo to invite your comments on this proposal, either in the proposal issue or here. Would you find this feature useful? If adopted, would you add an Unwrap() error to your error types? Do you see any problems with this proposal, or are there changes that would make it more useful to you?

    Thanks!

  • Feature request: ability to set default formatter

    Feature request: ability to set default formatter

    Hi, thanks for maintaining this library

    I'd like to use this library to return errors to a caller that would prefer no newlines if possible (in the event of one error). That is doable and looks like this:

    func Foo() error {
    	var retErr *multierror.Error
    	for _, x := range getSlice() {
    		if err := bar(x); err != nil {
    			retErr = multierror.Append(retErr, err)
    		}
    	}
    	return decorateFormat(retErr.ErrorOrNil())
    }
    
    func decorateFormat(err *multierror.Error) *multierror.Error {
    	if err != nil {
    		err.ErrorFormat = multierror.ErrorFormatFunc(func(errs []error) string {
    			if len(errs) == 1 {
    				return errs[0].Error()
    			}
    			return multierror.ListFormatFunc(errs)
    		})
    	}
    	return err
    }
    

    Which is fine, but requires me to remember to set the listFormatFunc, I'm doing that in a separate "decorator" func because I will need to do this many times.

    I would like to make it possible to specify the "default" error func so I don't have to remember to set it. This way, whenever a new multierror.Error was created, it would automatically have the format function I want. Would you accept a change like that? If so, perhaps a default global (defaults to the current ListFormatFunc) is the way to go, but would defer to you. Thanks in advance!

  • replace hashicorp/errwrap with go1.13 native error wrapping

    replace hashicorp/errwrap with go1.13 native error wrapping

    The errwrap.Wrapf() function was deprecated in favor of native go 1.13 error wrapping in github.com/hashicorp/errwrap v1.1.0 (https://github.com/hashicorp/errwrap/pull/9).

    This updates the Prefix() function to use native error wrapping, which removes the dependency on github.com/hashicorp/errwrap.

Drop-in replacement for the standard library errors package and github.com/pkg/errors

Emperror: Errors Drop-in replacement for the standard library errors package and github.com/pkg/errors. This is a single, lightweight library merging

Dec 20, 2022
This structured Error package wraps errors with context and other info

RErr package This structured Error package wraps errors with context and other info. It can be used to enrich logging, for example with a structured l

Jan 21, 2022
Common juju errors and functions to annotate errors. Based on juju/errgo

errors import "github.com/juju/errors" The juju/errors provides an easy way to annotate errors without losing the original error context. The exporte

Dec 30, 2022
Linter for errors.Is and errors.As

erris erris is a program for checking that errors are compared or type asserted using go1.13 errors.Is and errors.As functions. Install go get -u gith

Nov 27, 2022
A drop-in replacement for Go errors, with some added sugar! Unwrap user-friendly messages, HTTP status code, easy wrapping with multiple error types.
A drop-in replacement for Go errors, with some added sugar! Unwrap user-friendly messages, HTTP status code, easy wrapping with multiple error types.

Errors Errors package is a drop-in replacement of the built-in Go errors package with no external dependencies. It lets you create errors of 11 differ

Dec 6, 2022
Errors - A lib for handling error gracefully in Go

?? Errors Errors 是一个用于优雅地处理 Go 中错误的库。 Read me in English ??‍ 功能特性 优雅地处理 error,嗯,

Jan 17, 2022
Go error library with error portability over the network

cockroachdb/errors: Go errors with network portability This library aims to be used as a drop-in replacement to github.com/pkg/errors and Go's standar

Dec 29, 2022
Wraps the normal error and provides an error that is easy to use with net/http.

Go HTTP Error Wraps the normal error and provides an error that is easy to use with net/http. Install go get -u github.com/cateiru/go-http-error Usage

Dec 20, 2021
A simple errors package that dynamically prepends the package name.

errors ?? Buy me a cookie What is this? A simple errors package that dynamically prepends the package name. How to install Open a terminal and run the

Jan 16, 2022
Package semerr helps to work with errors in Golang.
Package semerr helps to work with errors in Golang.

semerr Package semerr helps to work with errors in Golang. Const error An error that can be defined as const. var errMutable error = errors.New("mutab

Oct 30, 2022
A Nostress Errors Package For Golang

A Nostress Errors Package For Golang

Nov 2, 2021
Go package for errors with chained stack traces

errstack: Go errors with chained stack traces errstack is a Go package for creating errors with stack traces. It is heavily inspired by github.com/pkg

Nov 27, 2021
A Go package providing errors with a stack trace Read-only

Errors with a stack trace A Go package providing errors with a stack trace. Features: Based of github.com/pkg/errors with similar API, addressing many

Sep 23, 2022
A powerful, custom error package for Go

custom-error-go A powerful, custom error package for Go Detailed explanation: https://medium.com/codealchemist/error-handling-in-go-made-more-powerful

Apr 19, 2022
Golang errors with stack trace and source fragments.
Golang errors with stack trace and source fragments.

Golang Errors with Stack Trace and Source Fragments Tired of uninformative error output? Probably this will be more convenient: Example package main

Dec 17, 2022
Hierarchical errors reporting done right in Golang

Hierarchical errors made right Hate seeing error: exit status 128 in the output of programs without actual explanation what is going wrong? Or, maybe,

Nov 9, 2021
Golang errors with stacktrace and context

merry Add context to errors, including automatic stack capture, cause chains, HTTP status code, user messages, and arbitrary values. The package is la

Nov 19, 2022
The Emperor takes care of all errors personally
The Emperor takes care of all errors personally

The Emperor takes care of all errors personally. Go's philosophy encourages to gracefully handle errors whenever possible, but some times recovering f

Jan 9, 2023
eris provides a better way to handle, trace, and log errors in Go 🎆

eris Package eris provides a better way to handle, trace, and log errors in Go. go get github.com/rotisserie/eris Why you'll want to switch to eris Us

Dec 29, 2022