Photo by David Pupaza on Unsplash
Go error handling
Anyone who has done some development in Go will be familiar with the Go philosophy on error handling and the following pattern:
result, err := myFunc(args)
if err != nil {
// do something
}
Catching errors
As many Gophers, over time I have grown accustomed to the inevitability of Go’s errors and come to appreciate this explicit and sturdy way of handling errors. Up till recently, however, there was one aspect about Go errors that kept confusing me, namely how to catch *specific *errors.
Let’s look at the following example:
file, err := os.Open("myfile")
if err != nil {
// handle error
}
In a case like this, coming from a Python background, I would like to use try…catch… pattern, for instance, to create myfile should it not exist.
In idiomatic Go there are two solutions for this.
The first is to rewrite the code to first check whether the file exists (you can use os.Stat() for that) and, if not, create it — this is probably preferable in this case.
The second is to make use of errors.Is() — this can be used if there is no workaround (see below).
errors.Is()
Go’s error package has a function errors.Is()that enables you to test a certain error against a target, like so:
In this way, we are able to catch a specific case of the possible errors returned by os.Open() , namely “file does not exist”, as different from, for instance, “permission denied”.
However, it can be a bit tricky to find the correct target to test an error against. Whereas other languages like Python will print a specific code in the error output (in this case FileNotFoundError) which you can then use as an exception, Go will only print the string representation of the error:
open myfile: no such file or directory
In Go you can either test against errors you define yourself, or generic system errors, like the ones defined in package io/fs :
var (
ErrInvalid = errInvalid() // "invalid argument"
ErrPermission = errPermission() // "permission denied"
ErrExist = errExist() // "file already exists"
ErrNotExist = errNotExist() // "file does not exist"
ErrClosed = errClosed() // "file already closed"
)
Another case where these predefined error variables are useful, is the sql package, as even “no rows found” is an error in Go:
var ErrNoRows = errors.New("sql: no rows in result set")
This is a clear example of where you cannot work around the error and would want to specifically test it, since you would like to catch “no rows”, but still have the software fail at something like a connection error! Just have a look at an an example from the documentation:
errors.As()
Finally, there is also another function, errors.As() ,which lets you use the error target:
Output:
Failed at path: non-existing
For more information on this advanced variation of the pattern, have a look at the documentation!
Hi! 👋 I’m Tom. I’m a software engineer, a technical writer and IT burnout coach. If you want to get in touch, check out https://tomdeneire.github.io
Top comments (0)