113

I'm just getting started with Go. My code is starting to have a lot of this:

 if err != nil { //handle err } 

or this

 if err := rows.Scan(&some_column); err != nil { //handle err } 

Are there some good idioms/strategies/best-practices for checking and handling errors in Go?

EDIT to clarify: I'm not bellyaching or suggesting that the Go team come up with something better. I'm asking if I'm doing it right or have I missed some technique that the community came up with. Thanks all.

6
  • 4
    No, there's not really. That's an often discussed topic, and a sensible one. There were many evolution proposals too. The team's answer seems to be that it should not be a problem in a well written code. Commented Jun 6, 2013 at 13:24
  • Related: stackoverflow.com/questions/15397419/… Commented Jun 6, 2013 at 13:26
  • Note that this related question isn't really the same as this one. The answers are too specific. Commented Jun 6, 2013 at 13:27
  • There's also a rationale for this annoyance : it makes it harder to fast write a program but it also makes it harder to create bugs by simply rethrowing errors. Commented Jun 6, 2013 at 13:43
  • You can find Andrew Gerrand and Brad Fitzpatrick write the beginnings of a HTTP/2 client in Go in more or less similar fashion youtube.com/watch?v=yG-UaBJXZ80 Commented Apr 15, 2015 at 2:22

11 Answers 11

64

Your code is idiomatic and in my opinion it is the best practice available. Some would disagree for sure, but I would argue that this is the style seen all over the standard libraries in Golang. In other words, Go authors write error handling in this way.

Sign up to request clarification or add additional context in comments.

7 Comments

"Go authors write error handling in this way." Sounds good to me.
"Some would disagree for sure" : I'm not sure someone would say it's not the best practice available today. Some ask syntax sugar or other changes but today I don't think any serious coder would check errors otherwise.
@dystroy: OK, some say "it sux", others call it "errors are handled in return values. 70′s style.", and so on ;-)
@jnml Handling errors this way is a matter of language design, which is a highly controversial topic. Luckily there are dozens of languages to choose from.
What just kills me is how the same pattern is used for every single function call. This makes the code rather noisy in some places and is just screaming out for syntactic sugar to simplify the code without losing any information, which is essentially the definition of conciseness (which I believe is a superior attribute than verbosity, but that is arguably a contentious point). The principle is sound, but the syntax leaves a lot to be desired IMHO. However complaining is forbidden, so I'll just drink my kool-aid now ;-)
|
33

Six months after this question was asked Rob Pike wrote a blog post titled Errors are Values.

In there he argues that you don't need to program in the way presented by the OP, and mentions several places in the standard library where they use a different pattern.

Of course a common statement involving an error value is to test whether it is nil, but there are countless other things one can do with an error value, and application of some of those other things can make your program better, eliminating much of the boilerplate that arises if every error is checked with a rote if statement.

...

Use the language to simplify your error handling.

But remember: Whatever you do, always check your errors!

It's a good read.

7 Comments

The article is awesome, it basically introduces an object that can be in failed state, and if it is it will just ignore everything you do with it and stay in failed state. To me sounds like almost-monad.
@Waterlink Your statement is meaningless. Everything that has a state is almost-monad, if you squint at it a bit. Comparing it to en.wikipedia.org/wiki/Null_Object_pattern is more useful, I think.
Pike: "But remember: Whatever you do, always check your errors!" -- this is so 80s. Errors can occur anywhere, stop burdening programmers and adopt exceptions for Pete's sake.
That's awful. Not only is there no longer a single standard way to handle errors, but the proposed solution also makes sure that errors are silently ignored when someone is not familiar with that particular way of handling them.
@colm.anseo I don't see how exceptions are more burdensome, in particular in a language like Kotlin where all exceptions are unchecked. As a programmer, I can decide what level of my stack I want to handle a particular type of error. Maybe I want to handle it immediately at the lowest level, or maybe I want to let it bubble up to the top to catch as a fatal error, depending on context. This is clean and flexible, and in particular decidedly not burdensome. I put error handling where and ONLY where I want it to happen.
|
22

I would agree with jnml's answer that they are both idiomatic code, and add the following:

Your first example:

if err != nil { //handle err } 

is more idiomatic when dealing with more than one return value. for example:

val, err := someFunc() if err != nil { //handle err } //do stuff with val 

Your second example is nice shorthand when only dealing with the err value. This applies if the function only returns an error, or if you deliberately ignore the returned values other than the error. As an example, this is sometimes used with the Reader and Writer functions that return an int of the number of bytes written (sometimes unnecessary information) and an error:

if _, err := f.Read(file); err != nil { //handle err } //do stuff with f 

The second form is referred to as using an if initialization statement.

So with regards to best practices, as far as I know (except for using the "errors" package to create new errors when you need them) you've covered pretty much everything you need to know abut errors in Go!

EDIT: If you find you really can't live without exceptions, you can mimic them with defer,panic & recover.

Comments

3

I made a library for streamlined error handling and piping through a queue of Go functions.

You can find it here: https://github.com/go-on/queue

It has a compact and a verbose syntactic variant. Here is an example for the short syntax:

import "github.com/go-on/queue/q" func SaveUser(w http.ResponseWriter, rq *http.Request) { u := &User{} err := q.Q( ioutil.ReadAll, rq.Body, // read json (returns json and error) )( // q.V pipes the json from the previous function call json.Unmarshal, q.V, u, // unmarshal json from above (returns error) )( u.Validate, // validate the user (returns error) )( u.Save, // save the user (returns error) )( ok, w, // send the "ok" message (returns no error) ).Run() if err != nil { switch err { case *json.SyntaxError: ... } } } 

Please be aware that there is a little performance overhead, since it makes use of reflection.

Also this is not idiomatic go code, so you will want to use it in your own projects, or if your team agrees on using it.

1 Comment

Just because you can do this, doesn't mean it's a good idea. This looks like the Chain of Responsibility pattern, except perhaps harder to read (opinion). I would suggest it's not "idiomatic Go". Interesting, though.
3

The Go gods have published a "draft design" for error handling in Go 2. It aims to change the errors idiom:

Overview and Design

They want feedback from users!

Feedback wiki

Briefly, it looks like:

func f() error { handle err { fmt.Println(err); return err } check mayFail() check canFail() } 

UPDATE: The draft design has received a lot of criticism, so I drafted Requirements to Consider for Go 2 Error Handling with a menu of possibilities for an eventual solution.

Comments

2

A "strategy" for handling errors in golang and in other languages is to continuously propogate errors up the call stack until you are high enough in the call stack to handle that error. If you tried handling that error too early, then you will likely end up repeating code. If you handle it too late, then you will break something in your code. Golang makes this process super easy as it makes it super clear whether you are handling an error at a given location or propagating it up.

If you are going to be ignoring the error, a simple _ will reveal this fact very clearly. If you are handling it, then exactly which case of the error you are handling is clear as you will check for it in the if statement.

Like people said above, an error is actually just a normal value. This treats it as such.

Comments

0

Most in industry, are following standard rules mentioned in golang documentation Error handling and Go. And it also helps doc generation for project.

2 Comments

This is essentially a link only answer. I would suggest you add some content to the answer so that if the link were to become invalid, your answer would still be of use.
thank you for valuable comment.
0

Below is my take on reducing error handling on for Go, sample is for when getting HTTP URL parameters :

(Design pattern derived from https://blog.golang.org/errors-are-values)

type HTTPAdapter struct { Error *common.AppError } func (adapter *HTTPAdapter) ReadUUID(r *http.Request, param string, possibleError int) uuid.UUID { requestUUID := uuid.Parse(mux.Vars(r)[param]) if requestUUID == nil { adapter.Error = common.NewAppError(fmt.Errorf("parameter %v is not valid", param), possibleError, http.StatusBadRequest) } return requestUUID } 

calling it for multiple possible parameters would be as below:

 adapter := &httphelper.HTTPAdapter{} viewingID := adapter.ReadUUID(r, "viewingID", common.ErrorWhenReadingViewingID) messageID := adapter.ReadUUID(r, "messageID", common.ErrorWhenReadingMessadeID) if adapter.Error != nil { return nil, adapter.Error } 

This is not a silver bullet, the downside is if you had multiple errors you are only able to get the last error.

But in this instance it is relatively repetitive and low risk, hence I can just get the last possible error.

Comments

-1

You can clean up your error handling code for similar errors (since errors are values you have to be careful here) and write a function which you call with the error passed in to handle the error. You won't have to write "if err!=nil {}" every time then. Again, this will only result in cleaning up the code, but I don't think it is the idiomatic way of doing things.

Again, just because you can doesn't mean you should.

Comments

-1

goerr allows to handle errors with functions

package main import "github.com/goerr/goerr" import "fmt" func ok(err error) { if err != nil { goerr.Return(err) // returns the error from do_somethingN() to main() // sequence() is terminated } } func sequence() error { ok(do_something1()) ok(do_something2()) ok(do_something3()) return nil /// 1,2,3 succeeded } func do_something1() error { return nil } func do_something2() error { return fmt.Errorf("2") } func do_something3() error { fmt.Println("DOING 3") return nil } func main() { err_do_something := goerr.OR1(sequence) // handle errors fmt.Println(err_do_something) } 

1 Comment

Ick. Complicating/hiding error handling logic like this isn't a good idea IMO. The resultant code (which needs source pre-processing by goerr) is harder to read/reason about than idiomatic Go code.
-4

If you want precise control of errors, this may not be the solution, but for me, most of the time, any error is a show stopper.

So, I use functions instead.

func Err(err error) { if err!=nil { fmt.Println("Oops", err) os.Exit(1) } } fi, err := os.Open("mmm.txt") Err(err) 

2 Comments

Such messages should go to stderr rather than stdout, so just use log.Fatal(err) or log.Fatalln("some message:", err). Since almost nothing other than main should make such a decision to end the whole program (i.e. return errors from functions/methods, don't abort) in the rare case this is what you want to do it's cleaner and better to do it explicitly (i.e. if err := someFunc(); err != nil { log.Fatal(err) }) rather than via a "helper" function that is unclear about what it's doing (the name "Err" isn't good, it gives no indication it may terminate the program).
Learned new thing! Thank you @DaveC

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.