Backend

go-error-handling

cxuu/golang-skills · updated Apr 8, 2026

$npx skills add https://github.com/cxuu/golang-skills --skill go-error-handling
summary

In Go, errors are values — they are

  • created by code and consumed by code.
skill.md

Go Error Handling

Available Scripts

  • scripts/check-errors.sh — Detects error handling anti-patterns: string comparison on err.Error(), bare return err without context, and log-and-return violations. Run bash scripts/check-errors.sh --help for options.

In Go, 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:

// Bad: Concrete type can cause subtle bugs
func Bad() *os.PathError { /*...*/ }

// Good: Always return the error interface
func Good() error { /*...*/ }

Error Strings

Error strings should not be capitalized and should not end with punctuation. Exception: exported names, proper nouns, or acronyms.

// Bad
err := fmt.Errorf("Something bad happened.")

// Good
err := fmt.Errorf("something bad happened")

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 caseslog.Fatal or panic

To intentionally ignore: add a comment explaining why.

n, _ := b.Write(p) // never returns a non-nil error

For related concurrent operations, use errgroup:

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return task1(ctx) })
g.Go(func() error { return task2(ctx) })
if err := g.Wait(); err != nil { return err }

Avoid In-Band Errors

Don't return -1, nil, or empty string to signal errors. Use multiple returns:

// Bad: In-band error value
func Lookup(key string) int  // returns -1 for missing

// Good: Explicit error or ok value
func Lookup(key string) (string, bool)

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:

// Good: Error first, normal code unindented
if err != nil {
    return err
}
// normal code

Handle errors once — either log or return, never both:

Error encountered?
├─ Caller can act on it? → Return (with context via %w)
├─ Top of call chain? → Log and handle
└─ Neither? → Log at appropriate level, continue

Read references/ERROR-FLOW.md when structuring complex error flows, deciding between logging vs returning, implementing the handle-once pattern, or choosing structured logging levels.


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().

Read references/ERROR-TYPES.md when defining sentinel errors, creating custom error types, or choosing error strategies for a package API.


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.

Read references/WRAPPING.md when deciding between %v and %w, wrapping errors across package boundaries, or adding contextual information.

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 when naming sentinel errors (ErrFoo) or custom error types
  • Testing errors: See go-testing when testing error semantics with errors.Is/errors.As or writing error-checking helpers
  • Panic handling: See go-defensive when deciding between panic and error returns, or writing recover guards
  • Guard clauses: See go-control-flow when structuring early-return error flow or reducing nesting
  • Logging decisions: See go-logging when choosing log levels, configuring structured logging, or deciding what context to include in log messages
general reviews

Ratings

4.510 reviews
  • Shikha Mishra· Oct 10, 2024

    go-error-handling is among the better-maintained entries we tried; worth keeping pinned for repeat workflows.

  • Piyush G· Sep 9, 2024

    Keeps context tight: go-error-handling is the kind of skill you can hand to a new teammate without a long onboarding doc.

  • Chaitanya Patil· Aug 8, 2024

    Registry listing for go-error-handling matched our evaluation — installs cleanly and behaves as described in the markdown.

  • Sakshi Patil· Jul 7, 2024

    go-error-handling reduced setup friction for our internal harness; good balance of opinion and flexibility.

  • Ganesh Mohane· Jun 6, 2024

    I recommend go-error-handling for anyone iterating fast on agent tooling; clear intent and a small, reviewable surface area.

  • Oshnikdeep· May 5, 2024

    Useful defaults in go-error-handling — fewer surprises than typical one-off scripts, and it plays nicely with `npx skills` flows.

  • Dhruvi Jain· Apr 4, 2024

    go-error-handling has been reliable in day-to-day use. Documentation quality is above average for community skills.

  • Rahul Santra· Mar 3, 2024

    Solid pick for teams standardizing on skills: go-error-handling is focused, and the summary matches what you get after install.

  • Pratham Ware· Feb 2, 2024

    We added go-error-handling from the explainx registry; install was straightforward and the SKILL.md answered most questions upfront.

  • Yash Thakker· Jan 1, 2024

    go-error-handling fits our agent workflows well — practical, well scoped, and easy to wire into existing repos.