error-handling
SKILL.md
Error Handling
Implement idiomatic Go error handling patterns.
Quick Start
Basic error handling:
result, err := doSomething()
if err != nil {
return fmt.Errorf("do something: %w", err)
}
Sentinel error:
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) {
// Handle not found
}
Instructions
Step 1: Return Errors
Basic error return:
func ReadFile(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read file %s: %w", path, err)
}
return data, nil
}
Multiple return values:
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
Step 2: Wrap Errors with Context
Using fmt.Errorf with %w:
func ProcessFile(path string) error {
data, err := ReadFile(path)
if err != nil {
return fmt.Errorf("process file: %w", err)
}
if err := Validate(data); err != nil {
return fmt.Errorf("validate data: %w", err)
}
return nil
}
Error chain:
// Original error
err := os.Open("file.txt")
// Wrapped once
err = fmt.Errorf("open config: %w", err)
// Wrapped again
err = fmt.Errorf("initialize app: %w", err)
// Unwrap to check original
if errors.Is(err, os.ErrNotExist) {
// Handle file not found
}
Step 3: Use Sentinel Errors
Define sentinel errors:
var (
ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
func GetUser(id string) (*User, error) {
user, ok := cache[id]
if !ok {
return nil, ErrNotFound
}
return user, nil
}
Check with errors.Is:
user, err := GetUser("123")
if errors.Is(err, ErrNotFound) {
// Handle not found case
return nil
}
if err != nil {
// Handle other errors
return err
}
Step 4: Create Custom Error Types
Error type with fields:
type ValidationError struct {
Field string
Value interface{}
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed for %s: %s", e.Field, e.Msg)
}
func Validate(user *User) error {
if user.Email == "" {
return &ValidationError{
Field: "email",
Value: user.Email,
Msg: "email is required",
}
}
return nil
}
Check with errors.As:
if err := Validate(user); err != nil {
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Printf("Field %s failed: %s\n", valErr.Field, valErr.Msg)
}
return err
}
Step 5: Handle Errors Appropriately
Check immediately:
// Good
result, err := doSomething()
if err != nil {
return err
}
// Bad - don't defer error checking
result, err := doSomething()
// ... more code ...
if err != nil {
return err
}
Don't ignore errors:
// Bad
doSomething()
// Good
if err := doSomething(); err != nil {
log.Printf("warning: %v", err)
}
// Or explicitly ignore
_ = doSomething()
Common Patterns
Error Wrapping Chain
func LoadConfig() (*Config, error) {
data, err := readConfigFile()
if err != nil {
return nil, fmt.Errorf("load config: %w", err)
}
config, err := parseConfig(data)
if err != nil {
return nil, fmt.Errorf("load config: %w", err)
}
return config, nil
}
Multiple Error Returns
func ProcessBatch(items []Item) ([]Result, error) {
results := make([]Result, 0, len(items))
for _, item := range items {
result, err := process(item)
if err != nil {
return nil, fmt.Errorf("process item %s: %w", item.ID, err)
}
results = append(results, result)
}
return results, nil
}
Collecting Multiple Errors
type MultiError []error
func (m MultiError) Error() string {
var msgs []string
for _, err := range m {
msgs = append(msgs, err.Error())
}
return strings.Join(msgs, "; ")
}
func ValidateAll(users []*User) error {
var errs MultiError
for _, user := range users {
if err := Validate(user); err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
Panic and Recover
Use panic for programmer errors:
func MustCompile(pattern string) *regexp.Regexp {
re, err := regexp.Compile(pattern)
if err != nil {
panic(err) // Invalid pattern is programmer error
}
return re
}
Recover from panics:
func SafeExecute(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}()
fn()
return nil
}
Error Context
type contextError struct {
op string
err error
}
func (e *contextError) Error() string {
return fmt.Sprintf("%s: %v", e.op, e.err)
}
func (e *contextError) Unwrap() error {
return e.err
}
func withContext(op string, err error) error {
if err == nil {
return nil
}
return &contextError{op: op, err: err}
}
Error Handling in HTTP
func handler(w http.ResponseWriter, r *http.Request) {
user, err := getUser(r.Context(), r.URL.Query().Get("id"))
if errors.Is(err, ErrNotFound) {
http.Error(w, "User not found", http.StatusNotFound)
return
}
if errors.Is(err, ErrUnauthorized) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
if err != nil {
log.Printf("get user: %v", err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
}
Error Handling in Goroutines
func processAsync(items []Item) error {
errCh := make(chan error, len(items))
for _, item := range items {
go func(it Item) {
errCh <- process(it)
}(item)
}
// Collect errors
for range items {
if err := <-errCh; err != nil {
return fmt.Errorf("process failed: %w", err)
}
}
return nil
}
Best Practices
- Return errors, don't panic: Except for programmer errors
- Check errors immediately: Don't defer checking
- Wrap errors with context: Use fmt.Errorf with %w
- Use sentinel errors: For expected error cases
- Use custom types: For errors needing additional data
- errors.Is for sentinel: Check wrapped sentinel errors
- errors.As for types: Extract custom error types
- Don't ignore errors: Handle or explicitly ignore with _
- Error messages lowercase: Start with lowercase, no punctuation
- Add context: Include operation and relevant data
Anti-Patterns
Don't:
- Ignore errors silently
- Use panic for normal errors
- Return error strings (use error types)
- Check error strings (use errors.Is/As)
- Create errors with fmt.Sprintf (use fmt.Errorf)
- Wrap errors without %w (loses error chain)
Troubleshooting
Error not matching with errors.Is:
- Ensure using %w when wrapping
- Check sentinel error is same instance
Can't extract error type:
- Use errors.As, not type assertion
- Ensure error type is pointer
Lost error context:
- Wrap errors at each level
- Include operation name in wrap
Weekly Installs
5
Repository
armanzeroeight/…-pluginsGitHub Stars
26
First Seen
Feb 4, 2026
Security Audits
Installed on
claude-code5
opencode4
gemini-cli4
github-copilot4
codex4
replit3