go-error-handling
This skill guides agents writing Go error handling by choosing between sentinel errors, custom types, and wrapping strategies using fmt.Errorf with %w or %v.
Community author
- Agents
- Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode
- License
- Apache-2.0 (permissive)
- Pin
- 91f0c2epinned Oct 9, 2026
- Context cost
- about 1,400 tokenswhen the skill loads
- GitHub stars
- 171on cxuu/golang-skills
- Scripts
- 2 scripts
What it does
“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).”
Generated summary from the skill's own text
This skill guides agents writing Go error handling by choosing between sentinel errors, custom types, and wrapping strategies using fmt.Errorf with %w or %v. It covers when to return, wrap, log, or handle errors, how to structure error flow for readability, and validation via included shell scripts and reference documentation.
When to use it
- Writing Go functions that return errors or handle failures
- Deciding whether to wrap errors with %w or %v across packages
- Structuring error handling to avoid log-and-return anti-patterns
- Implementing errors.Is or errors.As matching for specific error types
When not to
- Handling panic and recovery patterns, use go-defensive instead
- Testing error semantics in unit tests, use go-testing instead
- Choosing log levels or structuring log messages, use go-logging instead
Install
These commands install from the publisher's repository and need no account.
skills CLI
npx skills add cxuu/golang-skills --skill go-error-handlingInstalls from the repository's default branch, which may have changed since the commit we checked.
GitHub CLI 2.90 or later
gh skill install cxuu/golang-skills go-error-handling@91f0c2eef559a3168f9d3c38f5f99936d472b508Installs the commit we checked (91f0c2e).
Copy by hand
Download the folder at 91f0c2e and put it in .claude/skills/go-error-handling for Claude Code and in .agents/skills/go-error-handling for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.
Copy for your agent
A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.
Prompt
Install the agent skill "go-error-handling" by cxuu. Source, pinned to commit 91f0c2eef559a3168f9d3c38f5f99936d472b508: https://github.com/cxuu/golang-skills/tree/91f0c2eef559a3168f9d3c38f5f99936d472b508/skills/go-error-handling 1. Download that folder at exactly this commit. Do not use the default branch. 2. Copy every file unchanged, keeping the folder structure, into .claude/skills/go-error-handling/ for Claude Code and .agents/skills/go-error-handling/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist. 3. Do not edit the files, and do not run any script in the folder unless I ask you to. 4. When you are done, list the files you copied and where you put them. Listing and checks: https://www.agentnative.dev/skills/cxuu/go-error-handling
SKILL.md at the pin
---
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
From cxuu/golang-skills at 91f0c2e, shown as plain text under its license: Apache-2.0 (permissive). The license text is in LICENSE.
View SKILL.md on GitHub at 91f0c2eFiles
- SKILL.md6.0 KB · a96d708917ef
- references/
- ERROR-FLOW.md4.1 KB · 2065bd7c74a0
- ERROR-TYPES.md3.6 KB · b84946eab875
- WRAPPING.md5.0 KB · fef87e4491fb
- scripts/
- check-errors-ast.go9.7 KB · 898c64ce8710
- check-errors.sh1.3 KB · 90769f402227
6 files, 30 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.
Skill folder on GitHub at 91f0c2eRelated in Coding workflow
golang-cli
This skill guides building Go CLI applications using Cobra for commands and flags, Viper for configuration layering, and Unix best practices.
by samberCommunity
Contains scripts
Claude Code · about 2.3K tokens · pinned 6 days ago
planning
This skill orchestrates multi-workflow projects with dependencies and shared data tables by creating a task graph in the orchestrator via the create-tasks tool.
by n8nOfficial publisher
Claude Code, Codex, Cursor +3 · about 2.4K tokens · pinned 6 days ago
progressive-building
This skill implements incremental workflow development for agents building or extending workflows.
by n8nOfficial publisher
Claude Code, Codex, Cursor +3 · about 1.4K tokens · pinned 6 days ago
workflow-builder
Builds and edits n8n workflows using the TypeScript SDK.
by n8nOfficial publisher
Claude Code, Codex, Cursor +3 · about 14.2K tokens · pinned 6 days ago
remotion-upgrade
This skill upgrades Remotion and its related packages to the latest stable version.
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 460 tokens · pinned 6 days ago
remotion-upgrade
This skill upgrades Remotion and related packages across a project, automatically detecting the package manager and workspaces.
by RemotionOfficial publisher
Claude Code, Codex, Cursor +3 · about 460 tokens · pinned 6 days ago