Wrapping Errors with Defer
I wanted to document a small pattern I use to avoid repetition in error handling in Go while still providing context as an error climbs up the call stack. I first became aware of this pattern back in 2018 while the Go team was discussing error handling proposals. I’m not exactly sure who first had this idea; the earliest example of it I can find is in a comment on a gist, but I’m fairly certain that’s not where I first saw it. (If anyone knows, please send me an email!)
In short, if you have code like this:
func foo(x string) (*MyStruct, error) {
if err := doSomething(); err != nil {
return nil, fmt.Errorf("foo %s: %v", x, err)
}
if err := doSomethingElse(); err != nil {
return nil, fmt.Errorf("foo %s: operation 2: %v", x, err)
}
return new(MyStruct), nil
}
You can rewrite it like this:
func foo(x string) (_ *MyStruct, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("foo %s: %v", x, err)
}
}()
if err := doSomething(); err != nil {
return nil, err
}
if err := doSomethingElse(); err != nil {
return nil, fmt.Errorf("operation 2: %v", err)
}
return new(MyStruct), nil
}
If you only have one or two spots in your function where you’re wrapping the error, then this approach is probably overkill. However, if you have a lot of early returns on error, this approach can avoid the repetition of constructing the error message. (I even encountered the annoyance of fixing the multiple return statements while drafting this post!)