JetBrains Guide: Elegant Go Error Handling – From error Interface to Go 1.26 Generics
This JetBrains guide covers Go error handling fundamentals, wrapping with %w, type assertions using errors.Is/As/AsType, joining errors, context cancellation causes, panic/recover boundaries, and best practices for robust error management in production systems.
Prerequisites
The guide assumes familiarity with Go basics. All example code is embedded; a companion repository error-handling is available in the JetBrains Go blog code samples for hands-on practice.
Mainstream Error Handling Techniques in Go
Returning Errors
Go treats errors as values. The built-in error interface is defined as:
type error interface {
Error() string
}Functions return errors as the last return value. Example ReadFile demonstrates checking input, wrapping errors with fmt.Errorf("open failed: %w", err) to preserve the error chain, and using defer f.Close() after error checking to avoid nil pointer dereference.
Panic and Recover
Panic/recover is not for routine errors. It should only handle truly unexpected, unrecoverable conditions (e.g., hardcoded regex compilation failure via regexp.MustCompile). The net/http server uses recover to isolate request goroutine panics. Recovery pattern:
defer func() { if r := recover(); r != nil { /* handle */ } }().
Logging Errors
Use standard log or structured slog (Go 1.21+). Libraries should avoid logging internally; return errors to callers.
Error Wrapping
Errors bubble up the call stack. Wrap with fmt.Errorf("context: %w", err) to add context while preserving the original error via Unwrap(). String concatenation ( errors.New("msg" + err.Error())) flattens the error and loses type information.
Unwrapping Wrapped Errors
errors.Unwrap(err)returns the immediate wrapped error. Repeated calls traverse the chain.
Checking Specific Error Types
The errors package provides three functions:
errors.Is()
Checks if an error matches a target sentinel error (e.g., fs.ErrNotExist) anywhere in the chain.
errors.As()
Checks for a specific error type and extracts it into a pointer variable. Requires pre-declared target pointer.
errors.AsType() (Go 1.26)
Generic, type-safe alternative: func AsType[E error](err error) (E, bool). Avoids reflection, enables compile-time checking, and scopes matched variables to the if-block. Recommended for new code.
if pathErr, ok := errors.AsType[*fs.PathError](err); ok {
log.Println("path error at:", pathErr.Path)
} else if linkErr, ok := errors.AsType[*os.LinkError](err); ok {
log.Println("link error during:", linkErr.Op)
}Joining Multiple Errors
errors.Join(Go 1.20) combines multiple errors into one. The joined error is a []error internally; errors.Unwrap returns nil. Use type assertion to access the slice: e, ok := err.(interface{ Unwrap() []error }).
Context-Based Error Handling
context.WithCancelCause(Go 1.20) attaches a custom error to context cancellation. Call cancel(myError); retrieve with context.Cause(ctx).
Best Practices
Use defer for Cleanup
Place defer after error checks to avoid nil pointer dereference.
Provide Specific Error Context
Always wrap errors with meaningful context (function name, parameters) rather than passing raw errors.
Use Panic/Recover Only When Necessary
Reserve panic for programmer errors (hardcoded invariants) or unrecoverable system failures (OOM). Expected failures (user input, network) must be returned as errors.
Choose Libraries with Good Error Handling
Audit third-party libraries for proper error wrapping and context propagation.
Create Custom Error Types When Appropriate
Implement Error() and optionally Unwrap(), Timeout(), and structured fields (like fs.PathError with Op, Path, Err).
Handling Specific Error Types
Network Errors
Use net.OpError.Temporary() to distinguish transient failures and implement retry logic (simple loop or exponential backoff).
I/O Errors
Most I/O functions return (n int, err error). Use the byte count n to resume interrupted operations. io.EOF signals successful end-of-stream; implementers must follow the documented semantics: return (n, EOF) or (n, nil) on final read, then (0, EOF) on next call.
…when Reader encounters end-of-file after reading non-zero bytes, it may return either err == EOF or err == nil . The next Read should return 0, EOF .
Common Pitfalls to Avoid
Ignoring Errors
Never assign errors to blank identifier _. Use linters to detect unchecked errors.
Passing Errors Without Wrapping Context
Always add context when propagating errors; even the function name helps trace the call chain.
Overly Generic Error Messages
Avoid vague messages like "database error". Include specific details or use structured custom error types.
Using Inappropriate Error Types
Error types carry metadata; choose types that enable errors.Is/As/AsType checks.
Missing Error Logging
Log errors at the point they are handled or when they cannot be propagated further (e.g., in main).
Using log.Fatal()
log.Fatalcalls os.Exit, skipping all deferred cleanup. Restrict to top-level main with no pending defers.
Insufficient Error Recovery Consideration
Don't always crash. Consider fallback logic, high-availability SLAs, and isolating failures to individual goroutines (as http.ListenAndServe does).
Conclusion
Go's error handling syntax is minimal, but mastery lies in contextual decision-making and disciplined propagation. This guide covers practical techniques, modern APIs (Go 1.20/1.26), best practices, and anti-patterns to build maintainable, debuggable systems.
Original article: https://blog.jetbrains.com/go/2026/09/02/how-to-handle-errors-in-go/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
TonyBai
Tony Bai's tech world (tonybai.com). Not satisfied with just "knowing how", we strive for mastery. Focused on Go language internals, high-quality engineering practices, and cloud‑native architecture, exploring cutting‑edge intersections of Go and AI. Gophers who pursue technology are welcome—follow me and evolve with Go.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
