# How `errors.As` Simplifies Error Handling with Specific Error Types in Go

> Learn how Go's errors.As simplifies error handling by replacing verbose type assertions with a type-safe check that traverses your error stack.

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

---

**The `errors.As` function and its modern generic counterpart `errors.AsType` eliminate boilerplate when extracting specific error types from wrapped error chains, replacing verbose type assertions with a single, type-safe check that automatically traverses the entire error stack.**

Go's error interface provides a universal contract for failure signaling, but production code frequently needs to inspect concrete error types like `*os.PathError` or `*net.OpError`. The JetBrains/go-modern-guidelines repository documents how modern Go versions streamline this pattern through standard library helpers that unwrap error chains automatically, making error inspection both safer and more readable.

## The Pre-Go 1.13 Problem with Type Assertions

Before Go 1.13, extracting a specific error type required manual type assertions that failed to handle wrapped errors. If a library returned an error wrapped with `fmt.Errorf("context: %w", err)`, a direct type assertion on the outer error would return `false` even when the underlying type matched.

This limitation forced developers to implement custom unwrapping loops or avoid error wrapping entirely, reducing the richness of error context that could be preserved across API boundaries.

## How `errors.As` Works in Go 1.13+

The standard library introduced **`errors.As`** in Go 1.13 to solve the unwrapping problem. This function walks the error chain created by `%w` wrapping or custom `Unwrap` methods, populating a target pointer with the first error that matches the requested type.

Consider handling a file operation error:

```go
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    // pathErr now holds the *os.PathError from the chain
    log.Printf("file=%s, op=%s", pathErr.Path, pathErr.Op)
}

```

While functional, this pattern requires a pre-declared variable and a pointer-to-target argument, creating visual noise especially when checking multiple error types within the same function scope.

## The Generic `errors.AsType` Improvement in Go 1.26

Starting with Go 1.26, the **`errors.AsType[T]`** generic function provides a concise alternative. According to the [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) entry for the *errors_as_type* guideline (lines 42-46), this helper "returns the matched error value and a boolean directly" without requiring separate temporary variables or pointer arguments.

The implementation eliminates the boilerplate pattern:

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

// After: Direct assignment in condition
if pathErr, ok := errors.AsType[*os.PathError](err); ok {
    handle(pathErr)
}

```

The repository's unit tests in [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go) (line 16) verify that this guideline appears in the generated documentation, confirming its status as a recommended modern practice. The underlying guideline structures are loaded and served via [`internal/guidelines/guidelines.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.go), which provides the `ListText` and `ExplainText` functions surfacing these recommendations to users.

## Comparing Error Handling Patterns

When handling multiple specific error types, the difference becomes more pronounced:

```go
func handleError(err error) {
    // Traditional approach requires declaration before the block
    var pathErr *os.PathError
    var netErr *net.OpError
    
    if errors.As(err, &pathErr) {
        fmt.Println("path error:", pathErr.Path)
    } else if errors.As(err, &netErr) {
        fmt.Println("network error:", netErr.Op)
    }
}

```

Using the generic alternative enables inline declarations within switch statements:

```go
func handleError(err error) {
    switch {
    case e, ok := errors.AsType[*os.PathError](err); ok:
        fmt.Println("path error:", e.Path)
    case e, ok := errors.AsType[*net.OpError](err); ok:
        fmt.Println("network op error:", e.Op)
    default:
        fmt.Println("generic error:", err)
    }
}

```

## Benefits of Modern Error Type Inspection

Using `errors.As` and particularly the generic `errors.AsType` provides three concrete advantages:

- **More readable**: The intent to "match this specific error type" is expressed in a single line without pre-declaration overhead
- **Safer traversal**: Both helpers automatically walk wrapped error chains via `Unwrap()`, guaranteeing that deeply nested errors are considered without manual looping logic
- **Less error-prone**: The generic version removes the need to remember pointer syntax and target variable types, leveraging compile-time type inference instead

## Summary

- **`errors.As`** (Go 1.13+) introduced automatic error chain traversal but requires pre-declared pointer variables
- **`errors.AsType[T]`** (Go 1.26+) eliminates boilerplate by returning the typed error and boolean directly, as documented in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json)
- Both functions handle wrapped errors automatically through the `Unwrap` interface
- The JetBrains/go-modern-guidelines repository identifies `errors.AsType` as the preferred pattern for new Go code via its `ExplainText` and `ListText` APIs

## Frequently Asked Questions

### What is the difference between `errors.As` and `errors.AsType`?

`errors.As` requires a pre-declared variable and a pointer to that variable as its second argument, evaluating whether the error chain contains a matching type. `errors.AsType` is a generic wrapper introduced in Go 1.26 that returns the matched error value and a boolean directly, eliminating the need for separate variable declarations and pointer syntax.

### When should I use `errors.AsType` instead of a standard type assertion?

Use `errors.AsType` whenever you need to check for specific error types that might be wrapped inside other errors. Standard type assertions only inspect the immediate error value and fail when errors are wrapped with `%w` formatting or custom `Unwrap` methods, whereas `errors.AsType` traverses the entire chain automatically.

### How does `errors.As` handle deeply wrapped errors?

Both `errors.As` and `errors.AsType` recursively call the `Unwrap()` method on each error in the chain until they find a match or exhaust the chain. This ensures that errors wrapped multiple levels deep—such as a `*os.PathError` wrapped by a network timeout error, then wrapped by a service layer error—are still discoverable without manual loop implementation.

### Where can I find the official guidelines for modern Go error handling?

The JetBrains/go-modern-guidelines repository contains structured recommendations in [`internal/guidelines/guidelines.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json), including the *errors_as_type* entry describing the `errors.AsType` pattern. These guidelines are exposed through the package's API defined in [`internal/guidelines/guidelines.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.go) and verified by tests in [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go).