Monday, February 9, 2026

Errors

    Profile Pic of Akash AmanAkash Aman

    Updated: February 2026

    Error Handling

    The Error Interface

    Concept: In Go, an error is not a special language construct or a "crash." It is simply any type that implements a predefined, single-method interface.

    Behavior: Anything that has an Error() string method satisfies this interface.

    go
    type error interface {
        Error() string
    }
    
    • Why: This design treats error handling as a regular control flow. By making it an interface, Go allows you to create highly complex custom error types while keeping them compatible with standard error-checking logic.

    Defining and Declaring Errors

    Concept: Creating a simple, static error message to represent a specific failure state.

    • Setup:
    go
    import "errors"
    
    // Declaring a "Sentinel" error
    var ErrNotFound = errors.New("resource not found")
    
    • Behavior: errors.New returns a pointer to a struct. This ensures that even if two errors have the same text, they are only equal if they are the exact same instance in memory.
    • Why: This allows for Sentinel Errorsβ€”specific variables you can check against using == or errors.Is to decide how the program should react to a specific failure.

    Dynamic Errors with Formatting (fmt.Errorf)

    Concept: Creating errors that include dynamic context (like a specific ID or username) at the time the error occurs.

    • Behavior:
    go
    func getUser(id int) error {
        return fmt.Errorf("user %d: connection failed", id)
    }
    
    • Why: Static errors are great for "what" happened, but dynamic errors are crucial for "where" and "to whom" it happened, which is vital for debugging.

    Error Wrapping and Context (%w)

    Concept: Nesting one error inside another to add a "layer" of context without losing the original error's identity.

    • Behavior: Use the %w (wrap) verb in fmt.Errorf.
    go
    // Wrapping an error
    err := pkg.DoSomething()
    if err != nil {
        return fmt.Errorf("database layer: %w", err)
    }
    
    • Why: It allows you to build an "error chain." You can describe that a "Save" failed because the "Database" failed because the "Network" was down.

    Unwrapping and Inspection (errors.Is and errors.As)

    Concept: Modern Go tools used to peak inside an error chain to find a specific cause.

    • Behavior:
      • errors.Is(err, target): Checks if any error in the chain matches a specific sentinel value.
      • errors.As(err, &target): Checks if any error in the chain is of a specific type and assigns it to the target.
    go
    // Is the original cause a "Not Found" error?
    if errors.Is(err, ErrNotFound) {
        // Handle 404
    }
    
    // Is the error a specific "PathError" type?
    var perr *os.PathError
    if errors.As(err, &perr) {
        fmt.Println(perr.Path) // Accessing fields unique to PathError
    }
    
    • Why: This solves the problem of "brittle" error checking. You no longer have to parse strings to see what went wrong deep inside a library.

    Custom Error Structs

    Concept: Defining a struct to hold rich metadata about an error, rather than just a string.

    • Setup:
    go
    type QueryError struct {
        Query string
        Err   error
        Code  int
    }
    
    func (e *QueryError) Error() string {
        return fmt.Sprintf("query %s failed: %v", e.Query, e.Err)
    }
    
    • Properties:
      • Exported Fields: Just like regular structs, fields starting with a capital letter are accessible to the caller.
      • Embedding: You can embed other types to compose complex error structures.
    • Why: Useful for API responses or complex systems where you need to pass back status codes, retry-after durations, or validation details along with the error message.

    Reference Guides

    Comparison: Errors vs. Exceptions

    FeatureGo ErrorsJava/Python Exceptions
    PhilosophyErrors are values (Normal flow).Exceptions are disruptions (Out of band).
    ControlExplicit if err != nil check.Implicit try/catch block.
    PerformanceVery low overhead (it's just a value).High overhead (requires stack traces).
    VisibilityClearly visible in the function signature.Can be hidden or "bubbled up" invisibly.

    Summary Table

    ConceptUsageDeveloper Impact
    errors.NewSentinel ErrorsAllows for "Equality" checks (==).
    fmt.ErrorfContextual ErrorsAdds dynamic info to log messages.
    %w VerbWrappingPreserves the original error for later inspection.
    errors.IsComparisonSafely checks for a specific error in a chain.
    Custom StructsRich MetadataAllows passing complex data (e.g., HTTP codes).

    Β© 2026 Akash Aman | All rights reserved