Skip to content
Skill Authoring
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

BOOST
From plugin
golang-skills
18120 skills
Install
$ npx -y skills add cxuu/golang-skills --skill go-error-handling --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/go-error-handling

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

go-error-handling.SKILL.md
name: go-error-handling
description: 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).
allowed-tools: Bash(bash:*)

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:

// 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 cases** — `log.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`](https://pkg.go.dev/golang.org/x/sync/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

---

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
Read more
Ships withgolang-skills

AI Agent Skills for writing idiomatic, production-quality Go code. 20 modular skills teach AI coding assistants Go best practices derived from: Google Go Style Guide Effective Go Uber Go Style Guide Go Wiki CodeReviewComments Skills are tuned following

Get the whole plugin
Stats
181
Stars
19
Forks
Maintained
Maintenance
HTML
Language
Apache-2.0
License
4mo ago
Last commit
8mo ago
Created
2d ago
Added

Repo: cxuu/golang-skills

Other skills on golang-skills.