10

Since Go 1.13 we have the ability to chain errors, unwrap them and check if any error in the chain matches any of expected errors via errors.Is() and errors.As().

To wrap an error all you have to do is use %w verb with fmt.Errorf() like below.

fmt.Errorf("Custom message: %w", err)

This is easy, it wraps err in another one with additional message. But let's say I need some more context than just a message. How do I wrap err in my own, structured, custom error? Using Go 1.13+ standard library only.

2 Answers 2

15

You may create a new error type which wraps other errors while providing additional structured information.

type MyError struct {
    Inner error
    // Provide any additional fields that you need.
    Message string
    AdditionalContext string 
}

// Error is mark the struct as an error.
func (e *MyError) Error() string {
    return fmt.Sprintf("error caused due to %v; message: %v; additional context: %v", e.Inner, e.Message, e.AdditionalContext)
}

// Unwrap is used to make it work with errors.Is, errors.As.
func (e *MyError) Unwrap() error {
    // Return the inner error.
    return e.Inner
}

// WrapWithMyError to easily create a new error which wraps the given error.
func WrapWithMyError(err error, message string, additionalContext string) error {
    return &MyError {
        Inner: err,
        Message: message,
        AdditionalContext: additionalContext,
    }
}

You need to implement Error and Unwrap interfaces to use the new features of errors.Is and errors.As.

Sign up to request clarification or add additional context in comments.

3 Comments

Perfect, this is what I wanted. Additionally I've checked std lib fmt.Errorf code and it does exactly this. It creates a struct with inner error and Unwrap method. I wish Unwrap method was part of an interface, but it seems it isn't.
Unwrap is part of an ad-hoc interface at golang.org/src/errors/wrap.go?s=372:400#L4.
I meant classical, exposed interface. Turns our there is no specific reason for that - they couldn't come up with a name. github.com/golang/go/issues/35306
-2

Errors are an interface (satisfied by error() string) So you can make another error type as:

type myCustomError struct {
  Message    string
  StatusCode int
}

func (m myCustomError) error() string {
  return fmt.Sprintf("Code %d: \tMessage: %s", m.StatusCode, m.Message) 
}

Now you can use an error that is thrown as a custom error with

_, err := doStuff()
var v myCustomError
if errors.As(err, &v) {
   // now v is parsed 
}

1 Comment

Question was not about creating customer error but wrapping existing error in the custom one to create an error chain, which can be later checked with errors.As and errors.Is functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.