Go handles errors using explicit error values instead of exceptions. The error
type is an interface with a single method:
type error interface {
Error() string
}
Any type implementing Error()
can be used as an error, allowing developers to create custom errors with meaningful context. This guide covers creating custom errors, implementing them, and advanced techniques for wrapping and unwrapping errors effectively.
Basic Error Handling in Go
Go's built-in error handling is based on returning an error type from functions. If an error is encountered, it is returned to the caller, who can decide what to do with it.
Go
package main
import (
"errors"
"fmt"
)
func main() {
// Call doSomething() and check if it returns an error
err := doSomething()
if err != nil {
fmt.Println("Error:", err)
}
}
func doSomething() error {
return errors.New("something went wrong")
}
In this example, errors.New
creates a simple error with a message. This is helpful for cases where you need a basic error, but it lacks context. For more complex scenarios, custom errors become essential.
Why Create Custom Errors?
Custom errors in Go are essential when:
- More Context is Required: Standard errors may not provide enough information. Custom errors allow you to embed additional data.
- Categorizing Errors: You might need to distinguish between different types of errors. For instance, a "file not found" error and a "permission denied" error could be categorized differently.
- Adding More Details: Custom errors can store metadata like error codes, timestamps, or the original cause of the error.
Custom errors give you the flexibility to add specific fields or methods that describe the error more precisely.
Creating Custom Errors in Go
To create a custom error in Go, you need to define a new type that implements the Error()
method. This method returns the error message as a string.
Example:
Go
package main
import (
"fmt"
)
// Custom error type that includes an error code and message
type MyError struct {
Code int
Message string
}
// Implementing the Error() method to satisfy the error interface
func (e *MyError) Error() string {
return fmt.Sprintf("Code %d: %s", e.Code, e.Message)
}
func main() {
err := doSomething()
if err != nil {
fmt.Println("Error:", err)
}
}
func doSomething() error {
return &MyError{
Code: 404,
Message: "Resource not found",
}
}
In this example, we’ve defined a custom error type MyError
that includes both a Code
and a Message
. The Error()
method formats these values into a string, making it easy to print or log the error.
Advanced Techniques for Handling Errors
1. Sentinel Errors and the errors.Is()
Method
A sentinel error is a predefined error value that you can compare to errors returned by functions. Go’s errors.Is()
method is useful when you need to check if an error is of a specific type or matches a known error.
Example:
Go
package main
import (
"errors"
"fmt"
)
// Predefined error variable for better error handling and comparison
var ErrNotFound = errors.New("resource not found")
func main() {
// Call doSomething() and check if it returns the specific error
err := doSomething()
// Using errors.Is() to compare the returned error with ErrNotFound
if errors.Is(err, ErrNotFound) {
fmt.Println("Error: Resource not found")
}
}
func doSomething() error {
// Returning a predefined error instead of creating a new one each time
return ErrNotFound
}
In this example, we define a sentinel error ErrNotFound
. We then use errors.Is()
to check if the error returned by doSomething()
matches this sentinel value.
2. Wrapping Errors for More Context
Go 1.13 introduced the %w
verb in fmt.Errorf()
for wrapping errors, enabling the preservation of the original error while adding more context.
Example:
Go
package main
import (
"errors"
"fmt"
)
// Predefined error for consistency and easier error comparison
var ErrNotFound = errors.New("resource not found")
func main() {
// Call doSomething() and check if it returns an error
err := doSomething()
if err != nil {
fmt.Println("Error:", err)
// Using errors.Is() to check if the error is ErrNotFound
if errors.Is(err, ErrNotFound) {
fmt.Println("The resource was not found.")
}
}
}
func doSomething() error {
// Calling fetchResource(), which may return an error
err := fetchResource()
if err != nil {
// Wrapping the error with additional context using %w for error chaining
return fmt.Errorf("failed to do something: %w", err)
}
return nil
}
func fetchResource() error {
// Simulating an error scenario by returning a predefined error
return ErrNotFound
}
In this example, fmt.Errorf()
is used to wrap the ErrNotFound
error with more context ("failed to do something"). The %w
verb ensures the original error is preserved, and you can still use errors.Is()
to check for the root cause.
3. Unwrapping Errors with Unwrap()
Go’s error system also allows you to unwrap errors. By implementing the Unwrap()
method in your custom error types, you allow deeper inspection of the underlying cause of an error.
Example:
Go
package main
import (
"errors"
"fmt"
)
// Custom error type that includes additional context: error code, message, and the original error
type MyError struct {
Code int
Message string
Err error
}
// The Error() method formats the error message, making it suitable for printing
func (e *MyError) Error() string {
return fmt.Sprintf("Code %d: %s", e.Code, e.Message)
}
func (e *MyError) Unwrap() error {
return e.Err
}
// Predefined error for a specific resource not being found
var ErrNotFound = errors.New("resource not found")
func main() {
err := doSomething()
if err != nil {
fmt.Println("Error:", err)
// Using errors.Is() to check if the error is ErrNotFound, even if wrapped
if errors.Is(err, ErrNotFound) {
fmt.Println("The resource was not found.")
}
}
}
func doSomething() error {
// Simulate an error returned by fetchResource() and wrap it in MyError
err := fetchResource()
if err != nil {
return &MyError{
Code: 500,
Message: "Something went wrong",
Err: err,
}
}
return nil
}
func fetchResource() error {
// Simulating a resource not found error
return ErrNotFound
}
In this example, MyError
implements the Unwrap()
method, which returns the original error (ErrNotFound
). This allows you to unwrap the error and inspect it for more context.
The errors.As()
function is useful when you want to extract the custom error type and access its specific fields.
Example:
Go
package main
import (
"errors"
"fmt"
)
// Custom error type containing additional details like code, message, and underlying error
type MyError struct {
Code int
Message string
Err error
}
// Error method returns the custom error message for MyError
func (e *MyError) Error() string {
return fmt.Sprintf("Code %d: %s", e.Code, e.Message)
}
func main() {
err := doSomething()
var mErr *MyError
if errors.As(err, &mErr) {
fmt.Println("Error:", mErr)
}
}
// Function that fetches resource and wraps an error if it fails
func doSomething() error {
err := fetchResource()
if err != nil {
return &MyError{
Code: 500,
Message: "Something went wrong",
Err: err,
}
}
return nil
}
var ErrNotFound = errors.New("resource not found")
func fetchResource() error {
return ErrNotFound
}
In this example, errors.As()
is used to check if the error is of type MyError
and extract its value.
Best Practices for Custom Errors
- Use Custom Errors When Necessary: Custom errors provide more detailed context, but they should be used only when they significantly improve error handling and debugging.
- Use Wrapping and Unwrapping: Wrapping errors with additional context allows for better error tracking. Use
Unwrap()
to gain deeper insights into the root cause. - Document Your Error Types: Always document the purpose and usage of custom error types so other developers can easily understand how they work.
- Prefer Error Values for Comparison: Use predefined, exported sentinel errors for easy and consistent error comparisons.
Conclusion
Custom errors in Go provide developers with a flexible and powerful tool to manage errors efficiently. By creating your own error types, wrapping errors with context, and unwrapping them when needed, you can build a robust error-handling mechanism that improves the overall quality of your Go applications. Whether you need more context for debugging, categorizing errors, or improving the clarity of your code, custom errors are an essential feature in Go.
Similar Reads
Go Tutorial Go or you say Golang is a procedural and statically typed programming language having the syntax similar to C programming language. It was developed in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google but launched in 2009 as an open-source programming language and mainly used in Google
2 min read
Go Programming Language (Introduction) Go is a procedural programming language. It was developed in 2007 by Robert Griesemer, Rob Pike, and Ken Thompson at Google but launched in 2009 as an open-source programming language. Programs are assembled by using packages, for efficient management of dependencies. This language also supports env
11 min read
time.Sleep() Function in Golang With Examples In Go language, time packages supplies functionality for determining as well as viewing time. The Sleep() function in Go language is used to stop the latest go-routine for at least the stated duration d. And a negative or zero duration of sleep will cause this method to return instantly. Moreover, t
3 min read
Learn Free Programming Languages In this rapidly growing world, programming languages are also rapidly expanding, and it is very hard to determine the exact number of programming languages. Programming languages are an essential part of software development because they create a communication bridge between humans and computers. No
9 min read
Golang Tutorial - Learn Go Programming Language This Golang tutorial provides you with all the insights into Go Language programming, Here we provide the basics, from how to install Golang to advanced concepts of Go programming with stable examples. So, if you are a professional and a beginner, this free Golang tutorial is the best place for your
10 min read
strings.Contains Function in Golang with Examples strings.Contains Function in Golang is used to check the given letters present in the given string or not. If the letter is present in the given string, then it will return true, otherwise, return false. Syntax:Â func Contains(str, substr string) bool Here, str is the original string and substr is t
2 min read
Interfaces in Golang In Go, an interface is a type that lists methods without providing their code. You canât create an instance of an interface directly, but you can make a variable of the interface type to store any value that has the needed methods.Exampletype Shape interface { Area() float64 Perimeter() float64}In t
3 min read
Top 10 Golang Project Ideas with Source Code in 2025 Golang, or Go, a programming language was created by Google. It's widely used for building different kinds of applications like websites and cloud services. The fastest way to master this language is by building projects related to it. This article introduces 10 beginner-friendly to medium-difficult
8 min read
fmt.Sprintf() Function in Golang With Examples In Go language, fmt package implements formatted I/O with functions analogous to C's printf() and scanf() function. The fmt.Sprintf() function in Go language formats according to a format specifier and returns the resulting string. Moreover, this function is defined under the fmt package. Here, you
2 min read
Top 10 Golang Frameworks in 2025 Golang (or Go) is an open-source compiled programming language that is used to build simple, systematic, and secure software. It was designed by Google in the year 2007 and has been readily adopted by developers all over the world due to its features like memory safety, structural typing, garbage co
9 min read