# Modern Go Error Handling Patterns: A Complete Guide to JetBrains' go-modern-guidelines

> Discover modern Go error handling patterns with JetBrains go-modern-guidelines. Learn to use errors.Is, errors.As, errors.Join, and errors.AsType for safer, maintainable Go code.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: best-practices
- Published: 2026-08-29

---

**JetBrains' go-modern-guidelines recommends replacing direct error comparisons with `errors.Is` and `errors.As` (Go 1.13+), aggregating multiple errors using `errors.Join` (Go 1.20+), and adopting `errors.AsType[T]` for generic type assertions (Go 1.26+) to produce safer, more maintainable code.**

The JetBrains/go-modern-guidelines repository defines authoritative conventions for contemporary Go development, focusing on modern Go error handling patterns that leverage standard library utilities introduced in recent releases. These recommendations, documented in [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) and enforced via [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json), replace fragile manual error inspection with composable, wrapping-aware APIs. By following these patterns, developers ensure their error handling remains correct even when errors traverse multiple layers of abstraction.

## Use errors.Is and errors.As for Sentinel and Typed Error Inspection (Go 1.13+)

According to the guidelines defined in [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) section 5.1, Go 1.13 introduced wrapping-aware error inspection that should replace direct equality checks and type assertions. The **`errors.Is`** function traverses error chains to detect sentinel values, while **`errors.As`** safely extracts typed errors from wrapped chains.

Replace direct equality checks with `errors.Is`:

```go
// Anti-pattern: Direct comparison fails on wrapped errors
if err == os.ErrNotExist {
    return nil
}

// Modern pattern: errors.Is walks the error chain
if errors.Is(err, os.ErrNotExist) {
    return nil
}

```

Replace manual type assertions with `errors.As`:

```go
// Anti-pattern: Type assertion breaks with wrapped errors
if e, ok := err.(*os.PathError); ok {
    handle(e)
}

// Modern pattern: errors.As unwraps automatically
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    handle(pathErr)
}

```

The `errors.Is` method respects custom `Is` methods on error types, ensuring that sentinel errors are detected even when wrapped with additional context. Similarly, `errors.As` provides safe type assertion across wrapped chains without manual unwrapping logic.

## Aggregate Errors with errors.Join (Go 1.20+)

The **`errors.Join`** function, available since Go 1.20 and documented in [`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md) section 5.2, provides the standard mechanism for combining multiple errors. This approach preserves original error values for downstream inspection via `errors.Is` and `errors.As`, unlike string concatenation which destroys type information.

```go
// Anti-pattern: Manual aggregation loses type information
var errs []error
if err := op1(); err != nil {
    errs = append(errs, err)
}
if err := op2(); err != nil {
    errs = append(errs, err)
}
if len(errs) > 0 {
    return fmt.Errorf("multiple errors: %v", errs)
}

// Modern pattern: errors.Join preserves error chain
var errs []error
if err := op1(); err != nil {
    errs = append(errs, err)
}
if err := op2(); err != nil {
    errs = append(errs, err)
}
if len(errs) > 0 {
    return errors.Join(errs...)
}

```

According to the specification in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json), `errors.Join` returns `nil` when the input slice is empty, eliminating the need for explicit length checks in many scenarios.

## Leverage errors.AsType[T] for Generic Type Assertions (Go 1.26+)

For Go 1.26 and later, the guidelines recommend **`errors.AsType[T]`** as a type-safe alternative to the traditional two-step `errors.As` pattern. This generic function returns both the typed error and a boolean success indicator, eliminating the need for pre-declared target variables.

```go
// Legacy pattern: Pre-declared variable required
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    handle(pathErr)
}

// Modern pattern: errors.AsType returns value and boolean directly
if pathErr, ok := errors.AsType[*os.PathError](err); ok {
    handle(pathErr)
}

```

As defined in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) entry `errors_as_type`, this pattern removes the ceremonial pointer-to-target syntax while maintaining full compatibility with wrapped error chains.

## Source Code Architecture

The error handling recommendations are enforced and tested across several key files in the repository:

- **[`FEATURES.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/FEATURES.md)**: Contains the human-readable specification for error handling patterns in sections 5.1 and 5.2, defining the `erris` and `errorsjoin` guidelines.
- **[`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json)**: Machine-readable configuration driving the CLI tool, containing entries for `errors_is`, `errors_join`, and `errors_as_type` with version constraints.
- **[`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go)**: Validates that these error handling recommendations appear correctly in CLI output for applicable Go versions.
- **[`plugin/skills/use-modern-go/SKILL.md`](https://github.com/JetBrains/go-modern-guidelines/blob/main/plugin/skills/use-modern-go/SKILL.md)**: Documentation for IDE integrations that invoke these guidelines.

## Summary

- **`errors.Is` and `errors.As`** (Go 1.13+): Replace direct error comparisons and type assertions with wrapping-aware inspection functions that traverse error chains.
- **`errors.Join`** (Go 1.20+): Accumulate multiple errors using the standard library function instead of custom string formatting to preserve type information for downstream analysis.
- **`errors.AsType[T]`** (Go 1.26+): Adopt the generic type assertion helper to eliminate temporary variables and pointer syntax when extracting typed errors from chains.

## Frequently Asked Questions

### When should I use errors.Is instead of direct error comparison?

Use **`errors.Is`** whenever you need to check if an error matches a specific sentinel value, especially when the error might have been wrapped with additional context using `fmt.Errorf` with the `%w` verb. Direct equality checks fail when errors are wrapped, whereas `errors.Is` traverses the entire error chain and respects custom `Is` methods defined on error types.

### How does errors.Join differ from manually concatenating error strings?

**`errors.Join`** preserves the original error values as a linked chain, allowing downstream code to use `errors.Is` or `errors.As` to inspect individual components. Manual string concatenation destroys type information and creates opaque errors that cannot be programmatically inspected. Additionally, `errors.Join` returns `nil` when given an empty slice, simplifying aggregation logic.

### What is the advantage of errors.AsType[T] over the traditional errors.As?

**`errors.AsType[T]`** eliminates the need to pre-declare a target variable and pass its pointer to the function. Instead, it returns the typed error value and a boolean success indicator directly, reducing boilerplate and opportunity for variable shadowing bugs. This generic approach, available in Go 1.26+, makes error type extraction more concise while maintaining the same unwrapping capabilities as `errors.As`.

### Which Go versions support these modern error handling patterns?

According to [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json), **`errors.Is`** and **`errors.As`** require Go 1.13 or later, **`errors.Join`** requires Go 1.20 or later, and **`errors.AsType[T]`** requires Go 1.26 or later. The repository's test suite in [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go) validates that these recommendations are only suggested when the target Go version meets these minimum requirements.