Combine multiple error strings

54,978

Solution 1

You could use the strings.Join() and append() function to acheive this slice.

example: golang playgorund

package main

import (
    "fmt"
    "strings"
    "syscall"
)

func main() {

    // create a slice for the errors
    var errstrings []string 

    // first error
    err1 := fmt.Errorf("First error:server error")
    errstrings = append(errstrings, err1.Error())

    // do something 
    err2 := fmt.Errorf("Second error:%s", syscall.ENOPKG.Error())
    errstrings = append(errstrings, err2.Error())

    // do something else
    err3 := fmt.Errorf("Third error:%s", syscall.ENOTCONN.Error())
    errstrings = append(errstrings, err3.Error())

    // combine and print all the error
    fmt.Println(fmt.Errorf(strings.Join(errstrings, "\n")))


}

This would output a single string which you can send back to the client.

First error:server1 
Second error:Package not installed 
Third error:Socket is not connected

hope this helps!

Solution 2

UPDATE for Go 1.13:

As of Go version 1.13, the language's errors package now supports error wrapping directly.

You can wrap an error by using the %w verb in fmt.Errorf:

err := errors.New("Original error")
err = fmt.Errorf("%w; Second error", err)

Use Unwrap to remove the last error added, and return what remains: previousErrors := errors.Unwrap(err)

Playground Example for errors.Unwrap

Two more functions, errors.Is and errors.As provide ways to check for and retrieve a specific type of error.

Playground Example for errors.As and errors.Is


Dave Cheney's excellent errors package (https://github.com/pkg/errors) include a Wrap function for this purpose:

package main

import "fmt"
import "github.com/pkg/errors"

func main() {
        err := errors.New("error")
        err = errors.Wrap(err, "open failed")
        err = errors.Wrap(err, "read config failed")

        fmt.Println(err) // "read config failed: open failed: error"
}

This also allows additional functionality, such as unpacking the cause of the error:

package main

import "fmt"
import "github.com/pkg/errors"

func main() {
    err := errors.New("original error")
    err = errors.Wrap(err, "now this")

    fmt.Println(errors.Cause(err)) // "original error"
}

As well as the option to output a stack trace when specifying fmt.Printf("%+v\n", err).

You can find additional information about the package on his blog: here and here.

Solution 3

String functions don't work on errors because error is really an interface that implements the function Error() string.

You can use string functions on err1.Error() and err2.Error() but not on the "err1" reference itself.

Some errors are structs, like the ones you get from database drivers.

So there's no natural way to use string functions on errors since they may not actually be strings underneath.

As for combining two errors:

Easy, just use fmt.Errorf again.

fmt.Errorf("Combined error: %v %v", err1, err2)

Alternatively:

errors.New(err1.Error() + err2.Error())

Solution 4

To expand on what @WillC had mentioned in a comment it is possible to define your own error type as error is an interface type. Any type that implements a Error() string function implements the error interface. Therefore, you could create a CollectionError which aggregates errors and returns a concatenated error string.

type ErrorCollector []error

func (c *ErrorCollector) Collect(e error) { *c = append(*c, e) }

func (c *ErrorCollector) Error() (err string) {
    err = "Collected errors:\n"
    for i, e := range *c {
        err += fmt.Sprintf("\tError %d: %s\n", i, e.Error())
    }

    return err
}

This provides a collection function that appends a given error to a slice. Upon calling Error() string it iterates over the slice and creates a concatenated error string.

func main() {
    collector := new(ErrorCollector)

    for i := 0; i < 10; i++ {
         collector.Collect(errors.New(fmt.Sprintf("%d Error", i)))
    }

    fmt.Println(collector)
}

There is a great golang.org blog post going over errors in more detail. A full example of the example is available on The Go Playground.

Solution 5

People may be interested in https://github.com/hashicorp/go-multierror which describes itself as "A Go (golang) package for representing a list of errors as a single error.".

Share:
54,978

Related videos on Youtube

Greg Petr
Author by

Greg Petr

Updated on July 09, 2022

Comments

  • Greg Petr
    Greg Petr almost 2 years

    I am new to golang, my application needs to return multiple errors in a loop, later requires to be combined and returned as a single error string. I am not able to use the string functions to combine the error messages. What methods can be use to combine these errors into a single error before returning ?

    package main
    
    import (
       "fmt"
       "strings"
    )
    
    func Servreturn() (err error) {
    
       err1 = fmt.Errorf("Something else occured")
       err2 = fmt.Errorf("Something else occured again")
    
       // concatenate both the error
    
       return err3
    
    }
    
    • ZAky
      ZAky over 8 years
      Not enought information. What the client do? What your server do? A sample output.
  • Greg Petr
    Greg Petr over 8 years
    Any other ways other than using fmt.Errorf(), why cant I use the string function ?
  • kostix
    kostix over 8 years
    Sure. Depends on what you really mean by "combining". String concatenation is done using the + operator. Or you might store them individually in a slice, append()-ing values to it. This question suggests you should work yourself through a Go tutorial and get a book on it.
  • Will C
    Will C over 8 years
    @GregPetr Potentially another clean way to do this is to have your own CombinedError struct that takes a list of errors as a field and implements the Error() string method and do what you want, whether its concatenating strings or whatsoever.
  • Greg Petr
    Greg Petr over 8 years
    thanks, your example was very useful. I will try this out and get back.
  • Rakesh
    Rakesh about 6 years
    @BenCampbell Is there a reason for the method receiver to be a pointer here?
  • David
    David about 6 years
    Thanks for this tip. On the off chance one might add a nil error, the Error() function should do a nil check on the collected errors. And if one accesses by just index and not index + value, need to deference cast the error array before the index access. Here's an example: play.golang.org/p/h9F5WhcQ5Bs
  • dolmen
    dolmen about 4 years
    Wrapping is not for a multiple errors. It is for wrapping ONE error with more context.
  • dolmen
    dolmen about 4 years
    @Rakesh No. func (c *ErrorCollector) Error() (err string) should be func (c ErrorCollector) Error() (err string).
  • John Isaac
    John Isaac over 2 years
    You don’t need to, but it would violate convention to have some methods be pointer receivers and some to be value receivers. Collect needs to be a pointer receiver.
  • Paulo Neves
    Paulo Neves over 2 years
    @dolmen can you provide a citation on that assertion?
  • dolmen
    dolmen over 2 years
    @PauloNeves So far 17 persons approved my comment.
  • Paulo Neves
    Paulo Neves over 2 years
    @dolmen what is that supposed to mean?
  • igorushi
    igorushi over 2 years
    pay attention that if first error is nil e.g [nil, error1, error2, error3] you code will return nil because of the Wrap implementation ``` func Wrap(err error, message string) error { if err == nil { return nil } err = &withMessage{ cause: err, msg: message, } return &withStack{ err, callers(), } } ```
  • Sebastian
    Sebastian over 2 years
    Good point. This assumes that errs passed to the function are not nil.
  • Devin Burke
    Devin Burke over 2 years
    @Paulo: @dolmen is asserting that error wrapping is most intuitive when used like InnerExceptions in other languages. a wrapped error is not a list of (sibling) errors, but a chain of (parent-child) errors. there's nothing that stops you from using it like a list, but IMO it is counterintuitive.
  • dolmen
    dolmen almost 2 years
    If you agree with my statement, please downvote this answer to help bubble up more useful answers.