Go Error Handling Techniques

Go

Go Problem Overview


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.

Go Solutions


Solution 1 - Go

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.

Solution 2 - Go

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.

Solution 3 - Go

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.

Solution 4 - Go

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.

Solution 5 - Go

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.

Solution 6 - Go

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.

Solution 7 - Go

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

Solution 8 - Go

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.

Solution 9 - Go

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.

Solution 10 - Go

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)
}

Solution 11 - Go

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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongmooreView Question on Stackoverflow
Solution 1 - GozzzzView Answer on Stackoverflow
Solution 2 - GoMichael DeardeuffView Answer on Stackoverflow
Solution 3 - GoIntermernetView Answer on Stackoverflow
Solution 4 - GometakeuleView Answer on Stackoverflow
Solution 5 - Gohimanshu ojhaView Answer on Stackoverflow
Solution 6 - GoLiamView Answer on Stackoverflow
Solution 7 - GopschilakantiView Answer on Stackoverflow
Solution 8 - Gomel3kingsView Answer on Stackoverflow
Solution 9 - Go20kLeaguesView Answer on Stackoverflow
Solution 10 - GoAnlhord SmithsonView Answer on Stackoverflow
Solution 11 - GoGonView Answer on Stackoverflow