go-error-handling — Claude Code skill: what it does, how to install
Guides coding agents on idiomatic Go error handling, covering sentinel errors, error wrapping with %w or %v, and clean error flow.
npx skills add cxuu/golang-skills --skill go-error-handling -a claude-codeWhat go-error-handling does
The skill enforces explicit error handling patterns in Go. It stops agents from returning concrete error pointer types from exported functions, which prevents nil-interface bugs. It also bars in-band error returns like -1 or empty strings, requiring lowercase error strings without trailing punctuation.
It structures the choice of error strategy: use %v at external boundaries to hide implementation details, and use %w when callers need inspection via errors.Is or errors.As. To catch issues automatically, the package bundles scripts/check-errors.sh and scripts/check-errors-ast.go to scan AST code for string-based error matching or log-and-return anti-patterns.
Error unwrapping rules require Go 1.13+, and structured logging examples rely on log/slog from Go 1.21+. The skill intentionally omits panic and recover logic, leaving defensive guards to the separate go-defensive skill.
What it helps with
- choose between
%wand%vwhen wrapping errors across boundaries - eliminate log-and-return anti-patterns across call chains
- prevent concrete error pointer nil-interface bugs in exported APIs
- coordinate concurrent worker failures using
errgroup
Example prompts for the agent
- Review this Go package for log-and-return anti-patterns and fix them.
- Refactor this service method to return custom sentinel errors compatible with errors.Is.
- Run scripts/check-errors.sh to inspect our Go error handling logic.
How to install go-error-handling
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-error-handling -a claude-code2Manually: put the files in place
~/.claude/skills/go-error-handling/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/go-error-handling/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-error-handling -a codex2Manually: put the files in place
.agents/skills/go-error-handling/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-error-handling -a cursor2Manually: put the files in place
.cursor/skills/go-error-handling/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install cxuu/golang-skills go-error-handling2Manually: put the files in place
.github/skills/go-error-handling/SKILL.md1Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-error-handling -a gemini-cli2Platform CLI
gemini skills install https://github.com/cxuu/golang-skills --path skills/go-error-handlingManually: put the files in place
.devin/skills/go-error-handling/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add cxuu/golang-skills --skill go-error-handling -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about go-error-handling
How to install go-error-handling in Claude Code?
Run npx skills add cxuu/golang-skills --skill go-error-handling -a claude-code. Alternatively, add the skill file directly to ~/.claude/skills/go-error-handling/SKILL.md.
What does go-error-handling do?
It guides AI agents through Go error management, covering sentinel error creation, wrapping rules, and AST-based anti-pattern validation.
Does go-error-handling cover panic and recover?
No, it intentionally excludes panic and recover patterns. Those defensive patterns are covered in go-defensive.
Source text
SKILL.md · Apache-2.0
---
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:
```go
// 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.
```go
// 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.
```go
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):
```go
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:
```go
// 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:
```go
// 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
© cxuu · Apache-2.0 · cxuu/golang-skills