Oksa Satya.

April 20, 2026 · 6 min read

Error Handling in Go for Production APIs: Sentinels, Wrapping, and Safe Messages

GoError HandlingAPISecurity

In Go, errors are values — and how you treat them determines how easy the system is to debug at 3am. This is the error-handling pattern I use in production APIs so the trail is rich for developers, but the message is safe for users.

Wrapping keeps context without losing the origin

Returning a raw error upward loses context; swallowing it and making a new one loses the origin. The middle path is wrapping with %w — adding context at each layer while preserving the original chain.

With errors.Is and errors.As, the upper layer can still inspect the error type at the bottom of the chain, even after several wraps. Context grows, identity is not lost.

Sentinel errors for decisions, not string matching

Comparing errors by matching their message text is fragile code that breaks the moment the message changes. Sentinel errors (declared error variables) give a stable identity for conditions that need distinguishing — e.g. 'not found' vs 'already exists'.

This is what lets the HTTP layer decide 404 vs 409 without guessing from text — a type-based decision, not a string one.

Map to HTTP at a single choke point

Spreading HTTP status codes across every handler makes error behavior inconsistent. Better: one place that translates domain errors into HTTP responses — certain sentinels become 4xx with a safe, i18n-able message, the rest become 5xx with detail only in the log.

The user gets a clear message that does not leak internal structure; the developer gets a full trail in the log. Both win without sacrificing each other.

Summary

Good Go error handling: wrap with %w for context, use sentinels for type-based decisions, and map to HTTP in one place with safe messages for users and a rich trail for logs.

Building something similar?

I take on projects and technical consulting around business systems.