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)