Posted on • Originally published attomdeneire.Medium
Catching Go errors with errors.Is()
Photo byDavid Pupaza onUnsplash
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 usetry…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 functionerrors.Is()that enables you to test a certain error against atarget, 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 caseFileNotFoundError) 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 packageio/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 thesql 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 thedocumentation!
Hi! 👋 I’m Tom. I’m a software engineer, a technical writer and IT burnout coach. If you want to get in touch, check outhttps://tomdeneire.github.io
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse