Go Error Handling
Go - Error Handling
Section titled “Go - Error Handling”Go takes a distinct approach to error handling compared to languages that heavily rely on exceptions. Instead of try-catch blocks, Go functions that might fail typically return an additional value of type error alongside their primary result.
The error Type
Section titled “The error Type”error is a built-in interface type defined as:
type error interface { Error() string}Any type that implements the Error() string method satisfies the error interface. A nil error value indicates success, while a non-nil value indicates failure.
The Convention: Return error Last
Section titled “The Convention: Return error Last”By convention, functions that can produce an error return it as their last return value.
func DoSomething(input string) (ResultType, error) { // ... logic ... if /* something went wrong */ { return defaultValue, fmt.Errorf("failed to do something with %s: %w", input, specificError) } // ... success ... return successValue, nil // Return nil error on success}Checking for Errors
Section titled “Checking for Errors”The caller of a function that returns an error is responsible for checking the error value immediately after the call.
result, err := DoSomething("some input")if err != nil { // Handle the error (e.g., log it, return it, provide default) log.Printf("Error calling DoSomething: %v", err) return // Or return err, or handle differently}// If err is nil, proceed with using the 'result'fmt.Println("Success! Result:", result)This explicit if err != nil check is ubiquitous in Go code and makes error handling visible and mandatory.
Creating Errors
Section titled “Creating Errors”Go provides several ways to create error values:
errors.New(message string): Creates a simple error with a static text message. Good for basic, fixed error conditions.
import "errors"
var ErrNotFound = errors.New("resource not found")
func findResource(id string) error { if /* resource with id doesn't exist */ { return ErrNotFound } return nil}fmt.Errorf(format string, args ...any): Creates a formatted error message, similar tofmt.Sprintf. This is very flexible and commonly used.
import "fmt"
func processFile(filename string) error { _, err := os.Open(filename) if err != nil { // Create a more context-rich error message return fmt.Errorf("failed to open file '%s': %v", filename, err) } // ... process file ... return nil}Error Wrapping (Go 1.13+)
Section titled “Error Wrapping (Go 1.13+)”Modern Go encourages error wrapping to provide context without obscuring the original error. The %w verb in fmt.Errorf wraps the underlying error.
import ( "fmt" "os")
func readFileAndProcess(filename string) error { data, err := os.ReadFile(filename) if err != nil { // Wrap the error from os.ReadFile return fmt.Errorf("error processing file %s: %w", filename, err) } // ... process data ... return nil}The errors package provides functions to work with wrapped errors:
err := readFileAndProcess("nonexistent.txt")if err != nil { fmt.Println("Original error:", err)
// Check if the underlying error is specifically os.ErrNotExist if errors.Is(err, os.ErrNotExist) { fmt.Println("File not found error detected.") }
// Example with As (if we had a custom error type) // var customErr *MyCustomErrorType // if errors.As(err, &customErr) { // fmt.Println("Custom error code:", customErr.Code) // }}Example: Error Handling with Sqrt
Section titled “Example: Error Handling with Sqrt”Let’s refine the square root example to use fmt.Errorf and demonstrate the checking pattern.
package main
import ( "fmt" "math")
// Sqrt calculates the square root of a non-negative number.// It returns an error if the input value is negative.func Sqrt(value float64) (float64, error) { if value < 0 { // Use fmt.Errorf for a more informative error message return 0, fmt.Errorf("math: cannot calculate square root of negative number %g", value) } return math.Sqrt(value), nil // Return nil error on success}
func main() { fmt.Println("Attempting Sqrt(-1):") result1, err1 := Sqrt(-1) if err1 != nil { fmt.Println(" Error:", err1) } else { fmt.Println(" Result:", result1) }
fmt.Println("\nAttempting Sqrt(9):") result2, err2 := Sqrt(9) if err2 != nil { fmt.Println(" Error:", err2) } else { fmt.Println(" Result:", result2) }}Output:
Attempting Sqrt(-1): Error: math: cannot calculate square root of negative number -1
Attempting Sqrt(9): Result: 3Go’s explicit error handling, while sometimes verbose, leads to more robust and maintainable code by making error paths clear and requiring developers to confront potential failures.