Errors

Error Handling

In the Go programming language, errors are not treated as exceptions, and there is no concept of exception throwing.

Error Interface

Error handling is accomplished through the use of the error interface. The occurrence of an error does not necessarily lead to the immediate termination of the program. In contrast to some other languages, where attempting to open a non-existent file would trigger an exception, causing the application to throw an error and terminate if the exception is not caught, Go handles this differently. In Go, such an action does not result in a fatal error; instead, an error object is returned, allowing the developer to decide how to respond to the situation.

The error interface is used for organizing, propagating, and handling errors.

Its implementation looks like this:

type error interface {
    Error() string
}

From the code snippet above, you can see that the types are used for error handling must have a function named Error, which returns a string. This function will provide the error message.

type CustomError struct {
    Msg string
}
 
func (e CustomError) Error() string {
    return e.Msg
}
 
func main() {
    var Error error
    Error = CustomError{"error"}
  
    log.Fatal(Error)
}

CustomError is a correct implementation of the error interface so it can be assigned to the Error variable, which type is error interface.

Built in error functions

errors.New

The errors.New function creates a simple error with a static message. It’s part of the standard errors package.

package main
 
import (
	"errors"
	"fmt"
)
 
func main() {
	err := errors.New("something went wrong")
	fmt.Println(err)
}

Run this code in your browser (opens in a new tab) without installation.

Use errors.New for simple, static error messages where no dynamic data is needed.

fmt.Errorf

The fmt.Errorf function provides more flexibility by allowing formatted error messages, including dynamic values. It’s part of the fmt package.

func Errorf(format string, a ...interface{}) error
package main
 
import (
	"fmt"
)
 
func main() {
	userID := 42
	err := fmt.Errorf("user with ID %d not found", userID)
	fmt.Println(err)
}

Use fmt.Errorf when you need dynamic error messages or want to include contextual information.

Run the code without installation here (opens in a new tab)

Wrapping errors using fmt.Errorf

In Go, error wrapping is a best practice for adding context to errors while preserving the original error message. The most common way to wrap errors in Go is by using fmt.Errorf with the %w verb, which preserves the original error.

package main
 
import (
	"errors"
	"fmt"
)
 
func main() {
	baseErr := errors.New("file not found")
	wrappedErr := fmt.Errorf("failed to open config: %w", baseErr)
 
	fmt.Println(wrappedErr)
 
	// Unwrap the error
	originalErr := errors.Unwrap(wrappedErr)
	fmt.Println(originalErr) // Output: file not found
}

Run the code in your browser (opens in a new tab)

errors.As

The errors.As function is used to check if an error or any error in its chain matches a specific type. This is particularly useful when you need to handle custom error types.

package main
 
import (
	"errors"
	"fmt"
)
 
type CustomError struct {
	Msg string
}
 
func (e *CustomError) Error() string {
	return e.Msg
}
 
func main() {
	err := &CustomError{"Something went wrong"}
	var target *CustomError
 
	if errors.As(err, &target) {
		fmt.Println("Custom error detected:", target.Msg)
	} else {
		fmt.Println("Unknown error")
	}
}

Try it in your browser (opens in a new tab)

errors.Is

The errors.Is function is used to check if an error is equal to a specific error, including any error in the chain. It works well for error comparisons where you care about equivalence rather than type.

package main
 
import (
	"errors"
	"fmt"
)
 
var ErrNotFound = errors.New("not found")
 
func main() {
	wrappedErr := fmt.Errorf("operation failed: %w", ErrNotFound)
 
	if errors.Is(wrappedErr, ErrNotFound) {
		fmt.Println("The error is 'not found'")
	} else {
		fmt.Println("Different error")
	}
}

Run this code in your browser (opens in a new tab)

errors.Wrap

The errors.Wrap function (from the github.com/pkg/errors package) is used to add context to an error while preserving the original error for later inspection. Although Go 1.13 introduced fmt.Errorf with %w for error wrapping, errors.Wrap remains popular in some projects.

package main
 
import (
	"errors"
	"fmt"
 
	"github.com/pkg/errors"
)
 
func main() {
	err := errors.New("database connection failed")
	wrappedErr := errors.Wrap(err, "unable to load user data")
 
	fmt.Println(wrappedErr) // Prints: unable to load user data: database connection failed
}

Run the code above in your browser (opens in a new tab)

For Go 1.13+ projects, you can achieve similar wrapping with fmt.Errorf:

wrappedErr := fmt.Errorf("unable to load user data: %w", err)