Table of contents
1.
Introduction
2.
Collecting Detailed Information in a Custom Error
3.
Type Assertions and Custom Errors
4.
Wrapping Errors
5.
FAQs:
6.
Key Takeaways: 
Last Updated: Mar 27, 2024

Custom Errors in GO

Author NISHANT RANA
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

GO is an Open Source programming language also known as Golang or GO language. It was developed by Google Engineers. errors.New and fmt.Errorf are the two methods used in GO to generate errors. These two methods are not sufficient to capture or report what exactly happened in the program. To tackle this and get more functionalities we can implement the standard library interface, error.
 

Syntax: 

type error interface {
   Error() string
}

 

This error interface contains a single Error() method which returns the message of an error in the form of a String. By implementing this method we can generate an error of our own type.

 

Example:

package main


import (
"fmt"
"os"
)


type MyError struct{}


func (m *MyError) Error() string {
return "boom"
}


func sayHello() (string, error) {
return "", &MyError{}
}


func main() {
s, err := sayHello()
if err != nil {
fmt.Println("unexpected error: err:", err)
os.Exit(1)
}
fmt.Println("The string:", s)
}

 

Output:

unexpected error: err: boom
exit status 1

Collecting Detailed Information in a Custom Error

Mostly, custom errors are the best way to generate detailed information about the errors in the programs. 

Let us understand this with an example. We will try to capture the status code of the error produced by an HTTP request.

 

Example:

package main


import (
"errors"
"fmt"
"os"
)


type RequestError struct {
StatusCode int


Err error
}


func (r *RequestError) Error() string {
return fmt.Sprintf("status %d: err %v", r.StatusCode, r.Err)
}


func doRequest() error {
return &RequestError{
StatusCode: 503,
Err:        errors.New("unavailable"),
}
}


func main() {
err := doRequest()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("success!")
}

Output:

status 503: err unavailable
exit status 1

We created a new instance of the RequestError struct. We provided the status code and the error to this instance using the errors.New function. Then using the fmt.Println, we printed the error as done in the previous example.

In the Error() function of the RequestError struct, we have used the fmt.Sprintf() function to construct a string using the information provided when the error was created.

Type Assertions and Custom Errors

As discussed earlier, the error() interface contains only the single Error() method, but we need access to other methods of the error interface to handle an error property. For example, anyone can have many custom implementations of the error interface which are temporary and can be removed. It is denoted by the Temporary() method.
 

Interfaces provide a narrow view into the wider set of methods provided by types, so we must use a type assertion to change the methods that view is displaying, or to remove it entirely.

Interfaces provide a smaller view of all the methods provided by types. Hence, type assertion must be used to change the methods that view is displaying or to remove it entirely.

Example:

package main


import (
"errors"
"fmt"
"net/http"
"os"
)


type RequestError struct {
StatusCode int


Err error
}


func (r *RequestError) Error() string {
return r.Err.Error()
}


func (r *RequestError) Temporary() bool {
return r.StatusCode == http.StatusServiceUnavailable // 503
}


func doRequest() error {
return &RequestError{
StatusCode: 503,
Err:        errors.New("unavailable"),
}
}


func main() {
err := doRequest()
if err != nil {
fmt.Println(err)
re, ok := err.(*RequestError)
if ok {
if re.Temporary() {
fmt.Println("This request can be tried again")
} else {
fmt.Println("This request cannot be tried again")
}
}
os.Exit(1)
}


fmt.Println("success!")
}

Output:

unavailable
This request can be tried again
exit status 1

Explanation:

In the main function, we are calling the doRequest() function, which is returning the error interface to us, and then we are printing the error message we got from the Error() method. After this we are trying to expose all the methods by using the type assertion re, ok := err.(*RequestError). If the type assertion is successful, then we use the Temporary() method to check if the current error is a temporary error or not. The StatusCode set by the doRequest() method is 503, which is equivalent to http.StatusServiceUnavailable. Hence, it returns true and prints “This request can be tried again”.

Wrapping Errors

We can Wrap the errors with extra information at the starting of the error message to make it easier for debugging by providing some important context.

WrappedError is a struct with the following two fields:-

  1. String: A context message.
  2. error: It is the more information that it is providing.

Whenever the Error() method is called, we use fmt.Sprint() function to print the context message and then the error.

Example:

package main


import (
"errors"
"fmt"
)


type WrappedError struct {
Context string
Err     error
}


func (w *WrappedError) Error() string {
return fmt.Sprintf("%s: %v", w.Context, w.Err)
}


func Wrap(err error, info string) *WrappedError {
return &WrappedError{
Context: info,
Err:     err,
}
}


func main() {
err := errors.New("boom!")
err = Wrap(err, "main")


fmt.Println(err)
}

Output:

main: boom!

 

Explanation:

We are creating an error using the errors.New function in the main() function. Then we are wrapping that error using the Wrap() function defined by us. This indicates that the error was generated in the main() function.

FAQs:

  1. What is a Custom Error?
    Ans: A file is a space in the memory where we can store the data. A file can be a text file or a binary file.
  2. Why are errors.New and fmt.Errorf not sufficient and how can we tackle the problem with them?
    Ans: These two methods are not sufficient to capture or report what exactly happened in the program. To tackle this and get more functionalities we can implement the standard library interface, error.

Key Takeaways: 

In this article, we have extensively discussed the following things:

  1. We first discussed what are Custom Errors in GOlang.
  2. Then we discussed various ways to generate custom errors in GOlang.

We hope that this blog has helped you enhance your knowledge regarding Custom Errors in GO language and if you would like to learn more, check out our articles here. Do upvote our blog to help other ninjas grow. Happy Coding!







 

Live masterclass