# The error Interface
In Go, errors are values. The built-in error interface has just one method:
type error interface {
Error() string
}
// Creating errors
err1 := errors.New("something failed")
err2 := fmt.Errorf("failed to load %s", filename)# The if err != nil Pattern
Go's signature error-handling pattern. Return errors as the last return value and check immediately:
func readConfig(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("reading config: %w", err)
}
return data, nil
}
func main() {
data, err := readConfig("app.json")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Loaded", len(data), "bytes")
}# Custom Error Types
Create custom types that implement the error interface to carry additional context:
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 {
return &ValidationError{
Field: "age",
Message: "must be non-negative",
}
}
return nil
}# Wrapping & Inspecting Errors
Use %w to wrap errors and errors.Is() / errors.As() to inspect the chain:
import ("errors"; "io"; "os")
var ErrNotFound = errors.New("not found")
func findUser(id int) error {
return fmt.Errorf("findUser(%d): %w", id, ErrNotFound)
}
// Check the chain
err := findUser(42)
if errors.Is(err, ErrNotFound) {
fmt.Println("user not found")
}
// Check for specific type
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("path:", pathErr.Path)
}# panic & recover
panic is for unrecoverable errors. recover catches panics in deferred functions:
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v", r)
}
}
return a / b, nil
}
result, err := safeDivide(10, 0)
fmt.Println(result, err) // 0 panic: runtime error: ...⚡ Key Takeaways
- Errors are values, not exceptions — return and check them explicitly
- Wrap errors with
%wto preserve context along the call chain - Use
errors.Is()for sentinel checks anderrors.As()for type checks panicis for programmer bugs, not expected errors- Custom error types carry structured context beyond a message string