Agent SkillsAgent Skills
jovermier

go-error-handling

@jovermier/go-error-handling
jovermier
2
0 forks
Updated 4/6/2026
View on GitHub

Go error handling patterns including wrapping, custom error types, errors.Is/As, and error conventions. Use when handling, creating, or checking errors in Go.

Installation

$npx agent-skills-cli install @jovermier/go-error-handling
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Pathplugins/cc-go/skills/go-error-handling/SKILL.md
Branchmain
Scoped Name@jovermier/go-error-handling

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

npx agent-skills-cli list

Skill Instructions


name: go-error-handling description: Go error handling patterns including wrapping, custom error types, errors.Is/As, and error conventions. Use when handling, creating, or checking errors in Go.

Go Error Handling

Expert guidance for proper error handling in Go.

Quick Reference

OperationPatternExample
Wrap with contextfmt.Errorf with %wfmt.Errorf("opening file: %w", err)
Create custom errorstruct with Error()type ValidationError struct {...}
Check error typeerrors.Iserrors.Is(err, ErrNotFound)
Extract errorerrors.Aserrors.As(err, &validationErr)
Sentinel errorsvar at package levelvar ErrNotFound = errors.New("not found")
Ignore errorsNeverAlways check err != nil

What Do You Need?

  1. Error wrapping - Adding context to errors
  2. Custom error types - Creating structured errors
  3. Error inspection - errors.Is, errors.As
  4. Sentinel errors - Package-level error values
  5. Error conventions - When to wrap, return, or create

Specify a number or describe your error handling scenario.

Routing

ResponseReference to Read
1, "wrap", "context", "fmt.Errorf"wrapping.md
2, "custom", "type", "struct"custom-errors.md
3, "check", "errors.Is", "errors.As"inspection.md
4, "sentinel", "package", "global"sentinel.md
5, general error handlingRead relevant references

Critical Rules

  • Never ignore errors: Always check err != nil
  • Wrap with %w: Use %w to preserve error type for errors.Is
  • Wrap at boundaries: Wrap when crossing package boundaries
  • Don't wrap twice: Avoid double-wrapping the same error
  • Use errors.Is for sentinel: Check if error is a specific value
  • Use errors.As for types: Extract and inspect custom error types

Error Handling Patterns

Wrapping Errors

// Good: Wrap with context using %w
func (s *Service) Process(id string) error {
    item, err := s.repo.Find(id)
    if err != nil {
        return fmt.Errorf("finding item %s: %w", id, err)
    }
    // ...
}

// Bad: Wrapping with %v loses error type
return fmt.Errorf("finding item %s: %v", id, err)  // Can't use errors.Is()

// Bad: Double wrapping
return fmt.Errorf("processing: %w", fmt.Errorf("finding: %w", err))

Custom Error Types

// Define custom error type
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed for field %s: %s", e.Field, e.Message)
}

// Return custom error
func (s *Service) Validate(input Input) error {
    if input.Email == "" {
        return &ValidationError{
            Field:   "email",
            Message: "is required",
        }
    }
    return nil
}

Error Inspection

// Check for sentinel error
if errors.Is(err, ErrNotFound) {
    // Handle not found
}

// Extract and check custom error type
var validationErr *ValidationError
if errors.As(err, &validationErr) {
    // Access validationErr.Field, validationErr.Message
}

// Check multiple possibilities
if errors.Is(err, ErrNotFound) || errors.Is(err, ErrAccessDenied) {
    // Handle both cases
}

Sentinel Errors

// Package-level sentinel errors
var (
    ErrNotFound    = errors.New("not found")
    ErrAccessDenied = errors.New("access denied")
    ErrInvalidInput = errors.New("invalid input")
)

// Use in returns
func (r *Repository) Find(id string) (*Item, error) {
    // ...
    return nil, ErrNotFound
}

// Check in callers
if err != nil {
    if errors.Is(err, ErrNotFound) {
        return nil, nil  // Not found is not an error here
    }
    return nil, err  // Other errors are still errors
}

When to Wrap vs Return

ScenarioAction
Crossing package boundaryWrap with context
Internal functionReturn as-is
Adding retry logicDon't wrap (check with errors.Is)
Adding loggingLog then wrap or return
API layerWrap for user-friendly messages

Common Mistakes

MistakeSeverityFix
Ignoring errorsCriticalAlways check err != nil
Using %v instead of %wHighUse %w to preserve error type
Double wrappingMediumWrap only at boundary
Panicking on errorsCriticalReturn errors, don't panic
Creating strings for errorsLowUse errors.New() or sentinel
Wrapping nil errorMediumCheck err != nil before wrapping

Reference Index

FileTopics
wrapping.mdfmt.Errorf with %w, when to wrap
custom-errors.mdError types, methods, best practices
inspection.mderrors.Is, errors.As, type switches
sentinel.mdPackage-level errors, comparison

Success Criteria

Error handling is correct when:

  • No errors are ignored (all checked)
  • Errors wrapped at package boundaries with %w
  • Custom error types for domain-specific errors
  • Sentinel errors for expected conditions
  • errors.Is used for sentinel checking
  • errors.As used for type inspection
  • No panic on errors (except in package init/main)