← Back to Go Course | Chapter 9: Error Handling | Lesson 3 of 6

errors.Is and errors.As

errors.Is asks 'is this the exact same kind of problem I'm looking for?' while errors.As asks 'can I unwrap this into the specific error type I know how to handle?'

errors.Is for Sentinel Errors

errors.Is checks whether a target sentinel error appears anywhere in an error's wrap chain, correctly handling errors that have been wrapped multiple times, unlike a plain == comparison.

Example: errors.Is for Sentinel Errors

markup
package main

import (
	"errors"
	"fmt"
)

var ErrNotFound = errors.New("not found")

func find(id int) error {
	if id != 1 {
		return fmt.Errorf("lookup id %d: %w", id, ErrNotFound)
	}
	return nil
}

func main() {
	err := find(2)
	if errors.Is(err, ErrNotFound) {
		fmt.Println("item was not found")
	}
}

errors.As for Custom Types

errors.As walks the wrap chain looking for an error that matches a specific concrete type, and if found, assigns it into the target pointer so you can access its fields.

Example: errors.As for Custom Types

markup
package main

import (
	"errors"
	"fmt"
)

type RangeError struct {
	Value int
}

func (e *RangeError) Error() string {
	return fmt.Sprintf("value %d out of range", e.Value)
}

func validate(n int) error {
	if n > 100 {
		return fmt.Errorf("validate: %w", &RangeError{Value: n})
	}
	return nil
}

func main() {
	err := validate(150)
	var rangeErr *RangeError
	if errors.As(err, &rangeErr) {
		fmt.Println("bad value was:", rangeErr.Value)
	}
}

Why Not Use ==?

Once an error has been wrapped by fmt.Errorf's %w, the resulting error is a new value -- a plain == comparison against the original sentinel would fail even though logically it's 'the same' underlying error, which is exactly the problem errors.Is solves.

Note: Always prefer errors.Is/errors.As once wrapping is involved anywhere in a codebase.

Example: Why Not Use ==?

markup
package main

import (
	"errors"
	"fmt"
)

var ErrDenied = errors.New("access denied")

func main() {
	wrapped := fmt.Errorf("request failed: %w", ErrDenied)
	fmt.Println("== comparison:", wrapped == ErrDenied)
	fmt.Println("errors.Is:", errors.Is(wrapped, ErrDenied))
}
Common Mistakes
  1. Using == to compare a wrapped error against a sentinel error, which fails once fmt.Errorf %w has wrapped it.
  2. Confusing errors.Is (checks for a specific sentinel error value) with errors.As (extracts a specific error type).
  3. Forgetting that both functions walk the whole chain of wrapped errors, not just the outermost one.
Chapter Summary
  • errors.Is checks whether an error chain contains a specific sentinel error value.
  • errors.As checks whether an error chain contains a value of a specific error type and extracts it.
  • Both functions automatically walk through any wrapped errors created with fmt.Errorf's %w verb.
  • Prefer errors.Is/errors.As over direct == comparisons whenever errors might be wrapped.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.