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

  1. I want errors to be visible in the code, not hidden in a magic stack-unwinding mechanism that runs at distance.
  2. I want the type system to admit that errors are values.
  3. I want to be able to add context as the error travels up.
  4. 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.

← archive