← 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?'
Syntax
go
if errors.Is(err, target) {
    // matches target
}

var target *CustomError
if errors.As(err, &target) {
    // use target
}

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
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

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

// Define the function find
func find(id int) error {
	// Check whether id != 1
	if id != 1 {
		return fmt.Errorf("lookup id %d: %w", id, ErrNotFound)
	}
	// Send nil back to the caller
	return nil
}

// main is where the program starts running
func main() {
	// Short declaration: create err and infer the type
	err := find(2)
	// Check whether errors.Is(err, ErrNotFound)
	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
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

// Define a struct type named RangeError
type RangeError struct {
	Value int
}

// Define the method Error on type RangeError
func (e *RangeError) Error() string {
	// Sprintf builds a formatted string instead of printing it
	return fmt.Sprintf("value %d out of range", e.Value)
}

// Define the function validate
func validate(n int) error {
	// Check whether n > 100
	if n > 100 {
		return fmt.Errorf("validate: %w", &RangeError{Value: n})
	}
	// Send nil back to the caller
	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
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"errors"
	"fmt"
)

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

// main is where the program starts running
func main() {
	// Short declaration: create wrapped and infer the type
	wrapped := fmt.Errorf("request failed: %w", ErrDenied)
	// Print the values followed by a newline
	fmt.Println("== comparison:", wrapped == ErrDenied)
	// Print the values followed by a newline
	fmt.Println("errors.Is:", errors.Is(wrapped, ErrDenied))
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
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.