.agents/skills/go-error-handling/SKILL.md
Comprehensive Go error handling patterns from Google and Uber style guides. Covers returning errors, wrapping with %w, sentinel errors, choosing error types, handling errors once, error flow structure, and logging. Use when writing Go code that creates, returns, wraps, or handles errors.
npx skillsauth add phant-app/phant go-error-handlingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
In Go, errors are values - they are created by code and consumed by code. This skill covers how to return, structure, wrap, and handle errors effectively.
Normative: Required per Google's canonical Go style guide.
error TypeUse error to signal that a function can fail. By convention, error is the
last result parameter.
// Good:
func Good() error { /* ... */ }
func GoodLookup() (*Result, error) {
// ...
if err != nil {
return nil, err
}
return res, nil
}
Never return concrete error types from exported functions - a concrete nil
pointer can become a non-nil interface value:
// Bad: Concrete error type can cause subtle bugs
func Bad() *os.PathError { /*...*/ }
// Good: Always return the error interface
func Good() error { /*...*/ }
When a function returns an error, callers must treat all non-error return values as unspecified unless explicitly documented. Commonly, non-error return values are their zero values.
Tip: Functions taking a context.Context should usually return an error
so callers can determine if the context was cancelled.
Normative: Required per Google's canonical Go style guide.
Error strings should not be capitalized and should not end with punctuation:
// Bad:
err := fmt.Errorf("Something bad happened.")
// Good:
err := fmt.Errorf("something bad happened")
Exception: Error strings may start with a capital letter if they begin with an exported name, proper noun, or acronym.
Rationale: Error strings usually appear within other context before being printed.
For displayed messages (logs, test failures, API responses), capitalization is appropriate:
// Good:
log.Infof("Operation aborted: %v", err)
log.Errorf("Operation aborted: %v", err)
t.Errorf("Op(%q) failed unexpectedly; err=%v", args, err)
Normative: Required per Google's canonical Go style guide.
Code that encounters an error must make a deliberate choice about how to
handle it. Do not discard errors using _ variables.
When a function returns an error, do one of:
log.Fatal or (if absolutely necessary)
panicIn rare cases where ignoring an error is appropriate, add a comment explaining why:
// Good:
var b *bytes.Buffer
n, _ := b.Write(p) // never returns a non-nil error
When orchestrating related operations where only the first error is useful,
errgroup provides a
convenient abstraction:
// Good: errgroup handles cancellation and first-error semantics
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
}
Normative: Required per Google's canonical Go style guide.
Do not return special values like -1, nil, or empty string to signal errors:
// Bad: In-band error value
// Lookup returns the value for key or -1 if there is no mapping for key.
func Lookup(key string) int
// Bad: Caller mistakes can attribute errors to wrong function
return Parse(Lookup(missingKey))
Use multiple return values instead:
// Good: Explicit error or ok value
func Lookup(key string) (value string, ok bool)
// Good: Forces caller to handle the error case
value, ok := Lookup(key)
if !ok {
return fmt.Errorf("no value for %q", key)
}
return Parse(value)
This prevents callers from writing Parse(Lookup(key)) - it causes a
compile-time error since Lookup(key) has 2 outputs.
Normative: Required per Google's canonical Go style guide.
Handle errors before proceeding with normal code. This improves readability by enabling the reader to find the normal path quickly.
// Good: Error handling first, normal code unindented
if err != nil {
// error handling
return // or continue, etc.
}
// normal code
// Bad: Normal code hidden in else clause
if err != nil {
// error handling
} else {
// normal code that looks abnormal due to indentation
}
If you use a variable for more than a few lines, move the declaration out:
// Good: Declaration separate from error check
x, err := f()
if err != nil {
return err
}
// lots of code that uses x
// across multiple lines
// Bad: Variable scoped to else block, hard to read
if x, err := f(); err != nil {
return err
} else {
// lots of code that uses x
// across multiple lines
}
Advisory: Recommended best practice.
If callers need to distinguish different error conditions programmatically, give errors structure rather than relying on string matching. Choose the right error type based on whether callers need to match errors and whether messages are static or dynamic.
Quick decision table:
| 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 |
For detailed coverage of sentinel errors, structured error types, and error checking patterns, see references/ERROR-TYPES.md.
Advisory: Recommended best practice.
The choice between %v and %w significantly impacts how errors are propagated
and inspected:
%v: At system boundaries, for logging, to hide internal details%w: To preserve error chain for errors.Is/errors.As inspectionKey rules:
%w at the end: "context message: %w"err directlyFor detailed coverage of wrapping patterns, placement, adding context, and logging best practices, see references/WRAPPING.md.
Source: Uber Go Style Guide
When a caller receives an error, it should handle each error only once. Choose ONE response:
Never log AND return - this causes duplicate logging as callers up the stack will also handle the error.
// Bad: Logs AND returns - causes noise in logs
u, err := getUser(id)
if err != nil {
log.Printf("Could not get user %q: %v", id, err)
return err // Callers will also log this!
}
// Good: Wrap and return - let caller decide how to handle
u, err := getUser(id)
if err != nil {
return fmt.Errorf("get user %q: %w", id, err)
}
// Good: Log and degrade gracefully (don't return error)
if err := emitMetrics(); err != nil {
// Failure to write metrics should not break the application
log.Printf("Could not emit metrics: %v", err)
}
// Continue execution...
// Good: Match specific errors, return others
tz, err := getUserTimeZone(id)
if err != nil {
if errors.Is(err, ErrUserNotFound) {
// User doesn't exist. Use UTC.
tz = time.UTC
} else {
return fmt.Errorf("get user %q: %w", id, err)
}
}
| Pattern | Guidance |
|---------|----------|
| Return type | Always use error interface, not concrete types |
| Error strings | Lowercase, no punctuation |
| Ignoring errors | Comment explaining why it's safe |
| In-band errors | Avoid; use multiple returns |
| Error flow | Handle errors first, no else clauses |
| Error type choice | Match needed + dynamic → custom type; static → sentinel |
| Sentinel errors | Use errors.Is for checking |
| %v vs %w | %v for boundaries, %w for chain preservation |
| %w placement | Always at the end: "context: %w" |
| Handle once | Choose ONE: return, log+degrade, or match+handle |
| Logging | Don't log and return; let caller decide |
data-ai
Beginner-first implementation planning from an approved design. Use after brainstorming, or when the user already approved requirements and wants a step-by-step Laravel implementation plan before coding.
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
development
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
development
React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.