~/Advanced Error Handling in Go

Nov 17, 2020


Error handling in Go involves more than simply returning errors from functions. This article covers advanced techniques, including error wrapping, custom error types, stack tracing, error unwrapping, and best practices for production applications.

Error Wrapping and Unwrapping

Since Go 1.13, errors can be wrapped using the fmt.Errorf formatting function with the %w verb. This allows errors to maintain context, while still being identifiable by callers.

Example:

1
2
3
if err != nil {
    return fmt.Errorf("fetch failed: %w", err)
}

Unwrapping is enabled using errors.Is and errors.As functions which traverse wrapped error chains. For instance,

1
2
3
if errors.Is(err, sql.ErrNoRows) {
    // handle no rows
}

Custom Error Types

For custom behavior, define custom error types by implementing the error interface:

1
2
3
4
5
6
7
8
type ConfigError struct {
    Field string
    Msg   string
}

func (e *ConfigError) Error() string {
    return "config error: " + e.Field + ": " + e.Msg
}

Use errors.As to detect custom types in error chains.

Error Values and Sentinels

Package-level sentinel errors should rarely be exported. Use sentinels for situations where behavior must depend on exact error values.

1
var ErrTimeout = errors.New("timeout")

Returning this error allows clients to use errors.Is.

Error Context With Stack Tracing

Third-party libraries such as pkg/errors and xerrors provide stack trace capabilities, but starting with Go 1.13, standard library errors plus stack logging can usually suffice for diagnostics.

Best Practices

Handling Multiple Errors

For operations producing multiple errors, such as in bulk operations, aggregate errors using a custom error slice or using helpers like multierror:

1
2
3
4
5
6
7
var result error
for _, job := range jobs {
    if err := job(); err != nil {
        result = multierror.Append(result, err)
    }
}
return result

Logging Strategies

Integrate structured logging packages like zap or logrus for detailed error context logging.

Testing Error Handling

Use test helpers and table-driven tests to validate error paths, simulating failures with stubs or mocks.

Further Reading

By adopting the techniques above, developers achieve robust, maintainable, and context-rich error handling in Go applications.

Tags: [go] [errors] [programming]