go-error-handling
Use when writing Go code that returns, wraps, or handles errors — choosing between sentinel errors, custom types, and fmt.Errorf (%w vs %v), structuring error flow, or deciding whether to log or return. Also use when propagating errors across package boundaries or using errors.Is/As, even if the use
By cxuu · 1,097 installs
npx skills add cxuu/golang-skills --skill go-error-handling
Source repository · Upstream listing
Go Error Handling
Compatibility: errors.Is , errors.As , and %w wrapping require Go 1.13+; structured logging examples may use log/slog from Go 1.21+.
Resource Routing
scripts/check errors.sh Run when checking string based error matching, bare error propagation, and log and return patterns.
scripts/check errors ast.go Implementation helper invoked by check errors.sh ; patch this when changing error flow analysis behavior.
references/ERROR FLOW.md Read when deciding where to handle, wrap, log, or return errors.
references/ERROR TYPES.md Read when choosing sentinel errors, typed errors, or opaque errors.
references/WRAPPING.md Read when choosing %w versus %v or crossing package boundaries.
In Go, [errors are values](https://go.dev/blog/errors are values) — they are
created by code and consumed by code.
Choosing an Error Strategy
1. System boundary (RPC, IPC, storage)? → Wrap with %v to avoid leaking internals
2. Caller needs to match specific conditions? → Sentinel or typed error, wrap with %w
3. Caller just needs debugging context? → fmt.Errorf("...: %w", err)
4. Leaf function, no wrapping needed? → Return the error directly
Default : wrap with %w and place it at the end of the format string.
Core Rules
Never Return Concrete Error Types
Never return concrete error types from exported functions — a concrete nil
pointer can become a non nil interface:
Error Strings
Error strings should not be capitalized and should not end with
punctuation. Exception: exported names, proper nouns, or acronyms.
For displayed messages (logs, test failures, API responses), capitalization is
appropriate.
Return Values on Error
When a function returns an error, callers must treat all non error return values
as unspecified unless explicitly documented.
Tip : Functions taking a context.Context should usually return an error
so callers can determine if the context was cancelled.
Handling Errors
When encountering an error, make a deliberate choice — do not discard
with :
1. Handle immediately — address the error and continue
2. Return to caller — optionally wrapped with context
3. In exceptional cases — log.Fatal or panic
To intentionally ignore: add a comment explaining why.
For related concurrent operations, use
[ errgroup ](https://pkg.go.dev/golang.org/x/sync/errgroup):
Avoid In Band Errors
Don't return 1 , nil , or empty string to signal errors. Use multiple
returns:
This prevents callers from writing Parse(Lookup(key)) — it causes a
compile time error since Lookup(key) has 2 outputs.
Error Flow
Handle errors before normal code. Early returns keep the happy path unindented:
Handle errors once — either log or return, never both:
Error Types
Advisory : Recommended best practice.
Caller needs to match? Message type Use
No static errors.New("message")
No dynamic fmt.Errorf("msg: %v", val)
Yes static var ErrFoo = errors.New("...")
Yes dynamic custom error type
Default : Wrap with fmt.Errorf("...: %w", err) . Escalate to sentinels for
errors.Is() , to custom types for errors.As() .
Error Wrapping
Advisory : Recommended best practice.
Use %v : At system boundaries, for logging, to hide internal details
Use %w : To preserve error chain for errors.Is / errors.As
Key rules : Place %w at the end. Add context callers don't have. If
annotation adds nothing, return err directly.
Validation : After implementing error handling, run bash scripts/check errors.sh to detect common anti patterns. Then run go vet ./... to catch additional issues.
Related Skills
Error naming : See [go naming](../go naming/SKILL.md) when naming sentinel errors ( ErrFoo ) or custom error types
Testing errors : See [go testing](../go testing/SKILL.md) when testing error semantics with errors.Is / errors.As or writing error checking helpers
Panic handling : See [go defensive](../go defensive/SKILL.md) when deciding between panic and error returns, or writing recover guards
Guard clauses : See [go control flow](../go control flow/SKILL.md) when structuring early return error flow or reducing nesting
Logging decisions : See [go logging](../go logging/SKILL.md) when choosing log levels, configuring structured logging, or deciding what context to include in log messages