errors.Is and errors.As
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
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")
}
}
Login to try C/C++/Java/PHP code in the editor
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
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)
}
}
Login to try C/C++/Java/PHP code in the editor
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 ==?
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))
}
Login to try C/C++/Java/PHP code in the editor
- Using == to compare a wrapped error against a sentinel error, which fails once fmt.Errorf %w has wrapped it.
- Confusing errors.Is (checks for a specific sentinel error value) with errors.As (extracts a specific error type).
- Forgetting that both functions walk the whole chain of wrapped errors, not just the outermost one.
- 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: