anynines website
Lenny Händler

Lenny Händler

Published at 10.02.2023

Cloud Native

Errors Handling in GO – Bonus Content

After learning the basics of errors in go, and how to create an error with a particular message in the first chapter of this series.

We then discussed in the second chapter , how to enhance our error messages to provide better user information. We learned how to add additional information to our error messages while retaining the ability to distinguish between different error cases.

And finally, in our third part we talked about how to create your own error types, which can have any behaviour you may find helpful. And that was the end of our series. Or was it?

We brought here a bonus chapter with other uses cases to go deeper into handling errors in GO.

Other use cases for Custom error types

Of course you can add as many fields and functionality as you like, as long as your struct implements the error interface.

Depending on your application’s domain, you can add any additional information you may need. Some examples:

A numeric error code

Code Example

1type ErrorCode int
3func (c ErrorCode) Error() string {
4  return fmt.Sprintf("error %d", int(c))
A backoff time

Code Example

1type BackoffError time.Duration
2func (b BackoffError) Error() string{
3  Return fmt.Sprintf("please try again in %s", time.Duration(b))
6// usage example
7var err error = BackoffError(5*time.Second)
9var backoff BackoffError
10if errors.As(err, &backoff) {
11  time.sleep(time.Duration(backoff))
A message to display to the user

Code Example

1type DisplayError struct {
2  Message string
3  Error error
6func (e DisplayError) Error() string {
7  return e.Message
10func (e DisplayError) Is(err error) bool {
11  return errors.Is(err, e.Error)
14// Usage example
16var err error = DisplayError{Message: "Please try again later", Error: configprovider.ErrTimeout}
18if errors.Is(err, configprovider.ErrTimeout) { // true
19  fmt.Println(err) // "Please try again later"

… and many more. With custom error types the possibilities are endless. You can go even further and define your own interfaces for errors and add custom methods for any use case you might have. The sky’s the limit. This also shows the power of Go’s paradigm to make errors a normal type instead of a special case, like with exceptions in other languages.

const Sentinel errors

Sentinel errors as described above have one big downside, they are variables, and could be changed at runtime. Another reason we may want to avoid this is that two errors created using errors.New will not be equal even if they contain the same string. This is something you may want.

Code Example

1 fmt.Println(errors.New("hello") == errors.New("hello")) // false 

To avoid this we want to make them const. However, since we have to call errors.New to create them, that is not possible.

As we already learned, any type that has an Error() string func can be an error. This means we can create a simple error type that we can make const.

Code Example

1type Const string
3func (c Const) Error() string {
4  return string(c)

Which we can now use to create constant sentinel errors. For example:

Code Example

1package configprovider
3const (
4  ErrInvalidConfig = Const("invalid configuration")
5  ErrCannotLoad    = Const("cannot load configuration")

Which will also give us equality based on the content of the message

Code Example

1 fmt.Println(Const("hello") == Const("hello"))// true 

Constant errors

The story continues

Check out the full series here: Error Handling in Go


© anynines GmbH 2023