Skip to main content

🗓️ 02112024 2254
📎

GO ERROR HANDLING

Core Concept: Errors are values returned explicitly, not exceptions thrown implicitly.

Why It Matters

Forces explicit handling at each call site. No hidden control flow. Foundation of Go's reliability.

When to Use

Return error when:

  • Operation can fail
  • Caller decides handling

Don't return error when:

  • Programming errors (use panic)
  • Can handle internally

vs Java Exceptions

Java: try/catch (hidden control flow)
Go: Explicit if err != nil at each call

Go errors are values - can inspect, wrap, pass around.

WARNING

Wrapping without %w loses error chain. Use fmt.Errorf("context: %w", err) not %v. ```

Trade-offs

Pros: Explicit, clear flow, composable
Cons: Verbose, repetitive checks

Error wrapping builds on go_interfaces (error interface).

Quick Reference

// Basic pattern
result, err := operation()
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}

// Wrap errors
return fmt.Errorf("context: %w", err)

// Check specific error
if errors.Is(err, sql.ErrNoRows) { }

// Sentinel errors
var ErrNotFound = errors.New("not found")
PatternUseExample
errors.NewStatic errorerrors.New("failed")
fmt.ErrorfDynamic messagefmt.Errorf("id: %d", id)
%wWrap errorfmt.Errorf("save: %w", err)
errors.IsCheck typeerrors.Is(err, ErrNotFound)

Examples

EXAMPLE

Error wrapping with context:

func SaveUser(user *User) error {
if err := validate(user); err != nil {
return fmt.Errorf("validation failed: %w", err)
}

if err := db.Insert(user); err != nil {
return fmt.Errorf("failed to save user %s: %w", user.Name, err)
}

return nil
}

// Caller can check root cause
err := SaveUser(user)
if errors.Is(err, sql.ErrNoRows) {
// Handle specific database error
}

Sentinel errors for control flow:

var (
ErrNotFound = errors.New("user not found")
ErrInvalidData = errors.New("invalid user data")
)

func FindUser(id int) (*User, error) {
if id < 0 {
return nil, ErrInvalidData
}

user := db.Query(id)
if user == nil {
return nil, ErrNotFound
}

return user, nil
}

// Caller checks specific errors
user, err := FindUser(5)
if errors.Is(err, ErrNotFound) {
return nil, status.Error(codes.NotFound, "user not found")
}
if errors.Is(err, ErrInvalidData) {
return nil, status.Error(codes.InvalidArgument, "bad request")
}

```

References