From golang-skills
Guides Go error handling: sentinels vs custom types, fmt.Errorf wrapping (%w/%v), error flow, propagation across packages, errors.Is/As, with anti-pattern checker script.
npx claudepluginhub cxuu/golang-skills --plugin golang-skillsThis skill is limited to using the following tools:
- **`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.
Provides Go error handling patterns: wrapping with %w, sentinel errors, custom types, errors.Is/As. Use for implementing, designing error types, debugging chains, reviewing code.
Provides Go error handling patterns: basic errors, wrapping with %w, sentinel errors, custom types, errors.Is, and Unwrap for robust apps.
Implements and reviews Go error handling: creates/wraps errors with fmt.Errorf, checks via errors.Is/As/Join, uses sentinels/custom types, logs with slog. Audits codebases and PRs.
Share bugs, ideas, or general feedback.
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.
%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:
// Bad: Concrete type can cause subtle bugs
func Bad() *os.PathError { /*...*/ }
// Good: Always return the error interface
func Good() error { /*...*/ }
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.
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.
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 }
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.
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.
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.
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.
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.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