Skip to main content

Error Handling

In Go, error handling is explicit — meaning you have to manually check and handle errors instead of relying on exceptions (like in Java, Python, or C#). Errors in Go are values, and they follow the "error is just another return value" philosophy.

Go uses the built-in error interface for errors.

type error interface {
Error() string
}

If a function can fail, it usually returns two values:

  1. The result (if successful, otherwise zero value)
  2. An error (if something went wrong, otherwise nil)

Creating and Returning Errors​

We can create an error using the errors.New() function from the errors package or fmt.Errorf().

import (
"errors"
"fmt"
)

func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero is not allowed")
}
return a / b, nil
}

func main() {
result, err := divide(10, 0)

if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}

Using fmt.Errorf for Formatted Errors​

fmt.Errorf() is like errors.New() but allows formatting:

err := fmt.Errorf("failed to open file %s: %v", filename, originalErr)

Custom Error Types​

You can create your own error type by implementing the Error() method.

type MyError struct {
Code int
Message string
}

func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

func doSomething() error {
return &MyError{Code: 404, Message: "Not Found"}
}

func main() {
err := doSomething()
if err != nil {
fmt.Println(err)
}
}
  • Allows attaching extra information (like codes, timestamps, etc.)
  • Makes error checking more specific.

Error Wrapping (%w)​

Go 1.13+ supports error wrapping, letting you chain error contexts.

import (
"errors"
"fmt"
)

func readConfig() error {
return errors.New("file missing")
}

func loadApp() error {
return fmt.Errorf("loadApp failed: %w", readConfig())
}

func main() {
err := loadApp()
fmt.Println(err) // Prints: loadApp failed: file missing
}

Checking Specific Errors (errors.Is and errors.As)​

Sometimes you want to check what kind of error happened.

import (
"errors"
"fmt"
)

var ErrNotFound = errors.New("not found")

func search() error {
return ErrNotFound
}

func main() {
err := search()

if errors.Is(err, ErrNotFound) {
fmt.Println("Item not found.")
}
}

Panic vs Error​

  • Error → Recoverable problems; expected to happen sometimes.
  • Panic → Unrecoverable problems; program should stop immediately.
  • Use panic() rarely (e.g., initialization failures, corrupted state).
  • You can recover from panics using recover(), but that’s mainly for critical failure safety nets.