Go error handling, after eight years, is fine
An old post, kept up because people keep linking to it. I still mostly believe what's here.
Note (2026): this post is from 2023. Go 1.22 and 1.23 didn't
change my mind. I'd write it almost identically today, except I'd
be slightly more sympathetic to the people who don't like
if err != nil.
The complaint
Go's error handling is verbose. You write
if err != nil { return err } a lot. Some people find
this so annoying they leave the language over it. I've heard the
case made many times. I have not been moved.
What I actually want from error handling
- I want errors to be visible in the code, not hidden in a magic stack-unwinding mechanism that runs at distance.
- I want the type system to admit that errors are values.
- I want to be able to add context as the error travels up.
- I do not want clever syntax that lets me ignore errors by mistake.
Go gives me 1, 2, and 3. It is annoyingly enthusiastic about 4.
The if err != nil is, after eight years, a small price
I pay for getting the others.
The thing I do reach for
errors.Wrap from pkg/errors is mostly
obsoleted by fmt.Errorf("...: %w", err) in modern Go,
but the discipline is the same: at every layer, add context that
will help future-you debug. "couldn't open file" is useless. "in
handler X, while loading config Y, couldn't open file Z: actual
error" is gold.
That's it. That's the post.