Understanding errors.Join for Aggregating Errors in Go (Go 1.20+)

errors.Join provides a standard library mechanism for combining multiple error values into a single error while preserving the ability to inspect individual failures using errors.Is and errors.As.

Introduced in Go 1.20, errors.Join represents the modern approach to handling multiple concurrent failures without resorting to fragile string concatenation. According to the JetBrains/go-modern-guidelines repository, this function treats aggregated errors as a collection rather than a merged string, ensuring that non-nil errors remain distinguishable through Go's standard error-matching interfaces. This capability makes it indispensable for validation pipelines, concurrent operations, and any function that must report multiple distinct failures.

The Purpose and Behavior of errors.Join

The errors.Join function serves a specific need in Go error handling: it aggregates multiple errors into a single return value while maintaining the granularity required for proper error inspection. As documented in the repository's internal/guidelines/guidelines.json under the "errors_join" entry, this function returns nil only when all provided errors are nil, and otherwise returns a non-nil error that wraps each constituent error.

Unlike manual concatenation approaches, errors.Join creates an error tree that preserves the original error types and values. This design enables downstream code to use errors.Is to check for specific sentinel errors and errors.As to extract custom error types, regardless of how many errors were aggregated upstream.

Comparing errors.Join to Traditional Approaches

Before Go 1.20, developers typically aggregated errors using fmt.Errorf with format verbs, which either lost error identity or produced brittle string matching:

// Pre-Go 1.20 approach: loses granular error checking
if err1 != nil && err2 != nil {
    return fmt.Errorf("%v; %w", err1, err2)
}

The errors.Join approach eliminates these limitations by treating each error as a distinct node in the error chain. As noted in the repository's FEATURES.md file, this implementation provides first-class support for error aggregation that works seamlessly with Go's existing error inspection tools.

Practical Implementation Patterns

Basic Error Aggregation

The simplest use case involves combining two or more errors directly:

return errors.Join(err1, err2)

This syntax replaces verbose conditional blocks and ensures that both errors remain accessible to callers via errors.Is and errors.As.

Aggregating Dynamic Error Collections

When collecting errors from multiple operations in a loop or conditional chain, accumulate them in a slice and expand it using variadic syntax:

var errs []error
if err := doSomething(); err != nil {
    errs = append(errs, err)
}
if err := doAnotherThing(); err != nil {
    errs = append(errs, err)
}
return errors.Join(errs...)

This pattern, highlighted in the internal/guidelines/guidelines.go parsing logic, is particularly effective for validation functions that must report all constraint violations, not just the first failure encountered.

Inspecting Individual Errors in Joined Results

Once errors are joined, callers can still inspect specific error types using standard library functions:

if err := someFunc(); err != nil {
    if errors.Is(err, io.EOF) {
        // handle EOF specifically
    }
    if errors.As(err, &myCustomErr) {
        // handle custom error type
    }
}

The joined error implements the Unwrap() []error interface method, allowing errors.Is and errors.As to traverse the entire collection of aggregated errors.

Source Code References and Guidelines

The JetBrains/go-modern-guidelines repository provides authoritative documentation for errors.Join usage across three key locations:

  • internal/guidelines/guidelines.json – Contains the "errors_join" guideline entry that defines the standard practice for aggregating errors since Go 1.20
  • FEATURES.md – Tracks the implementation status and availability of the errors.Join feature within the guidelines ecosystem
  • internal/guidelines/guidelines.go – Implements the parser that presents the errors_join guideline data to tooling and documentation generators

These resources collectively establish errors.Join as the recommended replacement for manual error concatenation in modern Go codebases.

Summary

  • errors.Join combines multiple errors into a single return value without losing individual error identity
  • The function returns nil only when all input errors are nil, simplifying error checking logic
  • Aggregated errors remain fully compatible with errors.Is and errors.As for granular error inspection
  • The JetBrains/go-modern-guidelines repository documents this pattern in the "errors_join" guideline entry for Go 1.20+
  • This approach eliminates the need for manual string formatting using fmt.Errorf when collecting multiple failures

Frequently Asked Questions

When should I use errors.Join instead of fmt.Errorf?

Use errors.Join when you need to combine multiple independent errors that should remain inspectable as distinct entities, such as collecting validation failures or concurrent operation errors. Reserve fmt.Errorf with the %w verb for wrapping a single error with additional context, as it does not support aggregating multiple errors while preserving their individual identities.

How do I check if a specific error is present in a joined error?

Use the standard errors.Is or errors.As functions exactly as you would with a single wrapped error. Because errors.Join implements the Unwrap() []error interface, these functions automatically traverse the collection of aggregated errors and return true if any single error in the collection matches your check.

What happens if all errors passed to errors.Join are nil?

If all arguments are nil, errors.Join returns nil. This behavior allows you to safely aggregate errors from optional operations without additional nil checks, as a common pattern of appending errors to a slice and joining them at the end will naturally produce a nil result when no errors occurred.

Is errors.Join available in all Go versions?

No, errors.Join was introduced in Go 1.20. If you must support older Go versions, you will need to implement custom error aggregation using a slice-based error type that implements the Error() string method and Unwrap() []error interface, though this approach lacks the standard library optimizations and universal recognition of the built-in errors.Join implementation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →