go-errorslisted
Install: claude install-skill CasLubbers/code-design-skills
# Go errors
Errors are ordinary values. There is no exception mechanism to fall back on, so the error path is designed, not discovered.
## Wrap with context, keep the chain
Every layer adds what it was doing. `%w` preserves the original for inspection.
```go
// Good — reads as a path once printed: "load user: query user 42: sql: no rows in result set"
if err := rows.Scan(&u.ID, &u.Name); err != nil {
return nil, fmt.Errorf("query user %s: %w", id, err)
}
// Bad — chain broken, errors.Is downstream stops working
return nil, fmt.Errorf("query user: %v", err)
// Bad — no context, caller cannot tell which of six calls failed
return nil, err
```
Wrap when you add information. Returning `err` unchanged is correct when the callee's message already says everything.
## Error strings are lowercase and unpunctuated
They get embedded in other messages.
```go
errors.New("connection refused") // good
errors.New("Connection refused.") // bad — reads wrong once wrapped
```
Do not start with the word "error" or "failed to" — the context makes that obvious.
## Inspect with errors.Is and errors.As
Never compare with `==` on a wrapped error, and never type-assert directly.
```go
// Sentinel — a known condition callers branch on
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) {
return http.StatusNotFound
}
// Typed — the caller needs data out of the error
type ValidationError struct {
Field string
Reason string
}
func (e *Validatio