npx skills add ...
npx skills add cxuu/golang-skills --skill 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 user doesn't ask about error strategy. Does not cover panic/recover patterns (see go-defensive).
npx skills add cxuu/golang-skills --skill go-error-handling
Compatibility:
errors.Is,errors.As, and%wwrapping require Go 1.13+; structured logging examples may uselog/slogfrom Go 1.21+.
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 — they are created by code and consumed by code.
%v to avoid leaking internals%wfmt.Errorf("...: %w", err)Default: wrap with %w and place it at the end of the format string.
Never return concrete error types from exported functions — a concrete nil
pointer can become a non-nil interface:
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.
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.
When encountering an error, make a deliberate choice — do not discard
with _:
log.Fatal or panicTo intentionally ignore: add a comment explaining why.
For related concurrent operations, use
errgroup:
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.
Handle errors before normal code. Early returns keep the happy path unindented:
Handle errors once — either log or return, never both:
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().
Advisory: Recommended best practice.
%v: At system boundaries, for logging, to hide internal details%w: To preserve error chain for errors.Is/errors.AsKey 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.shto detect common anti-patterns. Then rungo vet ./...to catch additional issues.
ErrFoo) or custom error typeserrors.Is/errors.As or writing error-checking helpers