
Go error handling - reinhardt
https://code.ohloh.net/search?s=%22if%20err%20!%3D%20nil%22&pp=0&fe=go&mp=1&ml=0&me=1&md=1&ff=1&filterChecked=true
======
pilif
This higlights a point why I actually prefer exceptions to manually passing
around error codes.

If any of these calls to panic() are inside libraries, by using that library,
you will put yourself into a position where your application will exit without
giving you any recourse to fix it.

I've seen too many C libraries doing this too - "mmh - I can't handle this
right now - let me just call exit()" (last time this happened was inside a
commerical library for reading excel files. If it was confronted with a file
that was corrupt in some way, the library would call exit(), terminating my
application server in the process. Thanks)

With exceptions, a library can just throw but I as a consumer of the library
at least get a chance to handle the error somehow and who knows - I might be
able to find some way to still proceed and not crash.

If I don't handle that exception, oh well, then I crash anyways, but that's
not different from the library calling exit or panic.

With exceptions, I also never risk that I (or a library I use) forgets to
check for an exit code and proceeds to corrupt data (or crash way later at a
seemingly unrelated point).

When I fail to catch an exception, I my application dies.

When I fail to check an error code, my application might possibly corrupt
data.

Just because implementing exceptions in compiled languages is difficult and
messy (C++) and because exceptions are slow doesn't mean we shouldn't be using
them. It means that we have to improve our language and OSes in order for them
to work without hacks and quickly enough because, honestly, the value they
provide to me is big enough to warrant a bit of research.

~~~
Tobani
Panic is not exit.
[http://golang.org/pkg/os/#Exit](http://golang.org/pkg/os/#Exit) is exit

Panic is throw. Recover is an isomorphism for catch.

For the most part. You have to explicitly ignore error codes in go. (Not that
you can't handle them incorrectly.)

~~~
robryk
> You have to explicitly ignore error codes in go. (Not that you can't handle
> them incorrectly.)

I'm not sure I udnerstand what you mean. I can write: fmt.Fprint(os.Stdout,
"Something\n")

This returns an error, which I silently discard. Did you mean something else?

~~~
Tobani
fmt.Fprint(os.Stdout, "Something\n")

By itself isn't a valid statement. You try to compile that and you'll get an
error saying unused return value. So you'll try:

err := fmt.Fprint(os.Stdout, "Something\n")

and you could ignore err, but then you'll get a compiler saying err is defined
but never used. So you can be like NAH I'm just trying to muck stuff up so I"m
going to do this.

_ = fmt.Fprint(os.Stdout, "Something\n")

Which is explicitly discarding the error much like:

try{ fmt.Fprint(os.Stdout, "Something\n") }catch(Throwable t){}

~~~
robryk
I'm sorry, but that's not true:
[http://play.golang.org/p/PiMXQkKOef](http://play.golang.org/p/PiMXQkKOef)

~~~
Tobani
So it appears you're right. For things for which you don't care about any
value returned this is the case. The reason I haven't run into this is for the
most part things that can error, they also return a value that matters. You
can't use the return value without doing something with the error.

[http://play.golang.org/p/yreUbxx5Vv](http://play.golang.org/p/yreUbxx5Vv)

So it appears that there is a loophole for things that can error and purely
used for side effects.

------
NateDad
Here's why Go's errors are better than exceptions:

Multithreaded environments make exception handling next to impossible to do
correctly. The stack trace only shows a single thread which is often useless
without more context. Exceptions are per-thread, so handling has to be per-
thread.

Exceptions make the control flow of your program completely obscure. Any line
that calls out to another function can cause this function to exit. If you're
not wrapping every function call in a try/finally, you're probably doing it
wrong (at which point it's even more verbose than go's error returns).

Errors in Go aren't special. They're just values returned from a function. You
have the whole language at your disposal to deal with them.

I can write this in Go, and it's perfectly safe:

    
    
      f, err := os.Open(filename)
      if err != nil {
         return fmt.Errorf("Failed to open config file: %s", err)
      }
      err = callFunctionThatMightErrorOut()
      if err != nil {
        // handle err
      }
      f.Close()
    

I know that my code will ALWAYS close that file handle. To do the same thing
in a language with exceptions, you'd have to do something like this:

    
    
      File f
      try {
          try {
              f = os.Open(filename)
          }
          catch(Exception e) {
              throw new Exception(string.Format("Failed to open config file: %s", e.Message))
          }
          try {
              callFunctionThatMightErrorOut()
          }
          catch(Exception e) {
              // handle error
          }
      }
      finally {
          if f != nil {
              f.Close()
          }  
      }
    

Now who's verbose?

The reason people think Go exception handling is verbose is because they
aren't really handling errors in their own code. Go makes it easy to handle
errors in the right way.

~~~
dllthomas
Your exception code ignores RAII, which is about the best argument for
exceptions. The file should be closed as soon as the function returns for any
reason, which (with a sensible design and properly implemented destructors)
will be as soon as the function returns for any reason, meaning the outside
try-finally is entirely unnecessary and the file is guaranteed freed even if
you have multiple returns.

~~~
vardump
Golang has defer.
[http://golang.org/doc/effective_go.html#defer](http://golang.org/doc/effective_go.html#defer)

That's what grandparent post should have been using.

    
    
      f, err := os.Open(filename)
      if err != nil {
         return fmt.Errorf("Failed to open config file: %s", err)
      }
      defer f.Close() // This is going to be always executed when leaving this function
      err = callFunctionThatMightErrorOut()
      ...
    

When writing C++, I wish I had defer over RAII. In C++, when interfacing with
lower levels or C code, you need to first wrap everything in a class. Just so
that you can use unique_ptr or shared_ptr etc. Or worse, write a scoping
class. Ugh. Ugly and so many lines written for nothing...

~~~
dllthomas
The downside to defer over RAII, of course, is that there's no static
guarantee that you 1) remembered to include the defer, or 2) deferred the
right thing. I could see that getting painful in some refactorings. Which
isn't to say there aren't upsides.

~~~
vardump
Whenever you do forget, it's immediately obvious by looking at the function in
question. Or by grepping things that "open" something to see if there's
corresponding defer nearby.

In practise, it's not a big problem, because you learn fairly soon when doing
something reversible that a defer is needed to reverse it when leaving
scope/function. Like:

    
    
      func DoSomethingInSomeDir() error
      {
        err := os.Chdir("somedir")
        if err != nil { return err }
        defer os.Chdir("..") // always done even if there's panic
        ... do the something that can fail
        return nil
      }
    

Granted, using chdir in the first place is rather evil in anything but in a
simple utility. And yes, chdir("..") doesn't necessarily bring us back to the
starting position. And it itself can have error, which should be handled in
real code. But this is a quick example. :-)

But I didn't need to write a ChdirToSomewhereAndBack wrapper class either.
Control flow is completely obvious. All potential bugs are in plain sight, not
hidden in some wrapper.

If you saw this code for the first time ever, you'd have absolutely no
surprises or need to browse code in some class.

If you needed to do this chdir thing often, you could simply wrap it in a
function:

    
    
      func DoSomethingInANamedDir(dirName string, fn() error) error
      {
        err := os.Chdir(dirName)
        if err != nil { return err }
        defer os.Chdir("..") // always done even if there's panic
        return fn(); // do the something that can fail
      }
    

then just:

    
    
      DoSomethingingInANamedDir("something", 
        func() err { ... something that can fail ... })
    

Of course, as someone who sees this for the first time needs to look inside
DoSomethingingInANamedDir. Such is the price of wrappers.

~~~
dllthomas
_' Whenever you do forget, it's immediately obvious by looking at the function
in question. Or by grepping things that "open" something to see if there's
corresponding defer nearby.'_

That's not great. Static guarantees are worlds better than "I can manually
look at it, and manually grep for places I need to look".

 _" But I didn't need to write a ChdirToSomewhereAndBack wrapper class
either."_

That's not exactly hard, and if you're doing something frequently then writing
something to abstract it away is the right approach. It's true that many
languages leave defining that wrapper class ugly and push it far away from the
site of use in one-offs.

 _" All potential bugs are in plain sight, not hidden in some wrapper."_

That would be one of the upsides, yes. The related downsides are that there is
more room for bugs because you have to get it right every time instead of just
once, and bugs might be _hidden_ in plain sight when there is too much
clutter.

 _" If you needed to do this chdir thing often, you could simply wrap it in a
function:"_

That seems isomorphic to the RAII wrapper class, with slightly more syntactic
cruft.

~~~
vardump
"That's not great. Static guarantees are worlds better than "I can manually
look at it, and manually grep for places I need to look"."

Grepping or looking is pretty much what you need to do also in C++, if you
happen to have the bug in the wrapper. There's no static guarantee that can
figure out if there's no chdir back to original location.

~~~
dllthomas
If you have a bug in the wrapper, you fix it once in the wrapper, and it's
more likely to have shown up in a test if that wrapper is used multiple
places. There is no static guarantee that you wrote the wrapper right, but
hopefully there is some guarantee that you used the wrapper right. Definitions
can't outnumber uses (or errors in some of those definitions don't matter...).

------
raphael_kimmig
This is really interesting to me because it shows what to think of the claim
that explicit error handling makes go code more reliable than languages which
use exceptions.

When using go I often get the feeling of doing something wrong. Because I know
I'm supposed to "handle errors" but all I really want is the program to blow
up and hand me a stack trace. This gives me the worst of both worlds, I have
to manually trigger something that approaches the unhandled exception behavior
of other languages while retaining the verbosity of explicit errors returns.
Now it seems like I'm not the only person struggling with this.

Has anyone come up with a sane approach to that problem?

~~~
MetaCosm
I wouldn't say "more reliable" \-- I would say "easier to reason about".
Errors are just another returned variable, nothing more, nothing less. Go
doesn't encourage tracking two independent flow control sets -- one for
exception flow, and one for normal program flow. I have seen all sorts of
complex nastiness created via the multiple flow control sets in Java.

I write server code the runs for long periods of time, the amount of times I
want to blow up and get a stack trace is exceptional low, only when continuing
to run is more harmful than having the service completely down.

As for just wanting it to blow up -- just ignore the error handling, it will
blow up. Instead of putting the error in a variable, just throw it away using
_ ... and the next dependent thing will blow up. Not very elegant, but it will
remove all the boilerplate you don't like, and it will crash hard like you do
like... alternatively write a crashOnError(err error) function and call that
passing in error -- and all it has to do is if (err != nil) { panic(err) }

------
danielheath
It's natural that most public code panics on error, even though it's bad
practice.

Error return values (e.g. go) are better for building large systems where
stability & predictability are important (that is, cost to maintain).

Exceptions are better for quickly building systems (that is, cost to build).

~~~
threeseed
You have that completely backwards.

Exceptions are much better for large systems. You can define standardised
behaviour regardless of where an error occurs. You can let unexpected errors
'bubble up' and be managed in a way that preserves the integrity of the
system. You have the flexibility to recover from errors and let errors be
silenced.

There is unparalleled flexibility with exceptions. Go's approach is frankly
arcane.

~~~
MetaCosm
You are 100% right on unparalleled flexibility. Why limit your self to one set
of flow control operators when you can have two! Also, lets make one break out
of another one at any point of exception! The flexibility is endless!

You are 100% right, it is terrifyingly flexible. There is a reason exceptions
as flow control is considered an anti-pattern.

~~~
threeseed
So what you're saying is that the fundamental core of Java, Scala, Clojure are
anti-patterns ? That's a strong statement for a platform that almost every
enterprise application uses.

Go's error handling approach is also flow control. You receive an error and
have to go and do something different based on that error. With Go it is
explicit. With say Java you have the choice of being explicit or implicit.
Flexibility is vital when you are working on large applications where the
business logic varies wildly throughout the system.

~~~
NateDad
Yes, we're saying the fundamental core of Java is an anti-pattern. Exceptions
are dangerous. At any point in a function, you can suddenly jump out of that
function due to an exception. It's certainly _possible_ to write correct
programs with exceptions, but it's quite hard, and almost no one does it
correctly. If your code isn't chock full of try/finally blocks, then you're
not doing it right.

Explicit is way better than implicit by an order of magnitude. It means you
can look at any Go code and instantly understand what the control flow will
be. You can't do that with languages that have exceptions.

~~~
WorldWideWayne
Right - and we're saying that it's not. Exceptions handled incorrectly are not
anymore dangerous than incorrectly handling an error in Golang. At least with
exceptions, I can easily see the code that handles them in contrast to the
code that doesn't.

>> At any point in a function, you can suddenly jump...

That's kind of the point. There's a separate path for exceptional behavior,
which is what makes reasoning about exception paths easier.

>> It's certainly possible to write correct programs with exceptions, but it's
quite hard, and almost no one does it correctly.

You keep saying that, but where's the proof that Golang programs are any more
"correct"? How exactly are you quantifying that?

>> Explicit is way better than implicit by an order of magnitude.

That's like saying that having to think about putting one foot in front of the
other is better than just walking down the street. It's rather ridiculous to
say that.

>> It means you can look at any Go code and instantly understand what the
control flow will be.

Not at all because your control of flow is now so concerned with error
handling that you can't see the intended "happy path" logic.

~~~
NateDad
The whole point is that exception handling code, when written robustly, looks
almost like Go error handling code. That or you need to write a TON of magic
cleanup classes which take a lot of time, are even more error prone, and less
flexible than boring error handling code.

    
    
      f, err := os.Open(filename) 
      if err != nil {
          return fmt.Errorf("failed to open config file: %s", err)
      }
      // happy path
    

compare to:

    
    
      File f;
      try {
          f = File.Open(filename)
      }
      catch (Exception e) {
          throw new Exception(String.Format("failed to open config file: %s", e.Message))
      }
      // happy path
    
    

So, either you write code that looks like this, which is just like the Go
code, or you don't, and you're handling your errors in a poor fashion.

What most people do, is they would let this function throw
FileNotFoundException, and not catch it. There's at least three problems with
that:

1\. It leaks your implementation, so if later you're saving to memcache,
you'll throw some different error, which could break consumers that have
written code to handle the FileNotFoundException

2\. It lacks context: file not found: foo.yaml ... wtf is foo.yaml and why was
this random function trying to open it?

3\. You can't tell programmatically what failed. Something threw a
filenotfound... but you don't know if it was opening the config file or some
other function, so you can't handle it properly.

There's also the fact that now anyone who calls your function needs to wrap it
in a try/catch, or let an exception from 2 levels deep bubble up. So you can
get FileNotFound: foo.yaml from your Initiate() function, and no one knows
what that means.

That's what I mean by "almost no one [handles exceptions] correctly".

------
smcl
Maybe it would be better use the title of the submission to draw attention to
exactly what is interesting here. All I can see is "lots of people use 'err'
as the name of their returned error value in Go code on ohloh"

~~~
barrkel
Most of the time, the only things done with an error are fatal exit or panic -
the same code, over and over and over again.

It looks much worse than exceptions. (Except C++ and Java exceptions, as both
are exception anti-patterns in different ways.)

~~~
NateDad
I don't know what codebases this covers, but GOOD Go code does not panic.

~~~
sigzero
GOOD <insert your language here> code does not panic. Except people write
code...

~~~
dllthomas
But this seems more like "good Haskell code does not use unsafePerformIO" than
"good C code does not segfault" \- if you can find violations with a grep,
it's much less of a concern to rely on programmers to get it right and not use
it where inappropriate (which is not to say it's not a concern at all).

------
orangeduck
So I get it, and it makes a good point, but this could be more of a symptom
than a disease.

When I first started programming C I also tended to just exit the program on
getting a bad return code, because I'd never been taught how to properly
handle errors - I'd always just replied on exceptions.

Only later did I start to get bitten, and take proper care to handle errors
sensibly and explicitly.

As "go" is such a new language I wouldn't be surprised if a similar thing has
happened to new programmers migrating over to it.

~~~
cjslep
I find that I wind up "bubbling up" errors of my own to build a stack trace
yet still retain control of my program, like:

    
    
      func LowLevelCode() (int, error) {
        // rest of body...
        if /* error condition */ {
          return 0, errors.New("LowLevelCode error: " + /* error description */)
        }
      }
    
      func MiddleWare() (interface{}, error) {
        // body of code...
        nCount, err := LowLevelCode()
        if err != nil {
          return nil, errors.New("MiddleWare error: " + err.Error())
        }
      }
    
      func ApplicationCode() {
        // Do cool stuff...
        obj, err := MiddleWare()
        if err != nil {
          log.Println(err) // Prints "MiddleWare error: LowLevelCode error: " + ...
          // Handle on a high level, retry connections, etc.
        }
      }
    

I'm hoping someone comes in and corrects me with a more elegant way of
"building up your own stack trace" without having to do this manually and
without panic.

~~~
NateDad
There are a lot of error handling libraries out there. On Juju, we're working
on one right now. This is the preliminary version:
[https://github.com/juju/errgo](https://github.com/juju/errgo)

Basically you just call a function every time you would return an error, and
it automatically records the file and line number and lets you add on some
context to the message, plus it gives you the option to mask the type of the
error (to hide implementation details from your callers).

I wouldn't use errgo right now, it's still under heavy development, but it's
similar to a lot of other error handling libraries out there.

------
arianvanp
I like erlang's model here. Fail often and fail fast and let the supervisor of
that particular node just restart the thing. Bug fixing comes later when you
inspect your logs. Do errors bubble up in actors in go like they do in erlang?

~~~
tomp
How is that different from exceptions, really? It's just less granular, but
exactly what e.g. Django (a Python web framework) foes with exceptions (per-
request, not per-process).

------
Intermernet
Is this meant to be a negative comment on Go error handling not conforming to
a single pattern, or a positive comment on Go error handling not needing to
conform to a single pattern?

Or is it just a random code search posted on HN?

------
logicchains
I've found in some cases Go's style of error handling can actually make for
great documentation: when every third line of code is something like

    
    
      if err != nil{ 
        log.Printf("This specific operation %v went
           wrong because input %v was %v or was expected to be %v but 
           wasn't", operationName, inputName, actualInput,
           expectedInput)
      }
    

Then reading the error messages gives a pretty good idea of what's supposed to
be happening at that point in the code.

------
JensRantil
I don't see what's interesting with this?

~~~
norswap
I think it shows that while the Go people usually say that their error
handling is better than exception, most often they either exit on exception
(which is like not catching an exception) or pass the error up the call stack
(the default behaviour for an uncaught exception). This means that this way of
doing error handling is actually just verbose.

Which is also my opinion.

~~~
sagichmal

        > most often they either exit on exception (which is like
        > not catching an exception)
    

Not really. Exiting on error is an explicit decision that you make, hopefully
only in your top-level (func main) scoping block. Letting an exception bubble
up to that level without catching it is invisible.

    
    
        > or pass the error up the call stack (the default 
        > behaviour for an uncaught exception)
    

The crucial difference, which the Go team takes great pains to elucidate and
fans of exceptions never seem to acknowledge, is that by treating the error as
a first-order value, you make its propagation up the call stack explicit, in
the normal i.e. visible code path. That makes reasoning about your error path
a lot easier, and makes producing code that handles errors correctly a lot
simpler.

The "Eureka" of error handling in Go isn't a massive thing. It's just the
reification of the small and important understanding that errors are values,
just like anything else.

~~~
WorldWideWayne
So, your argument is that - because you actually _see_ some code that simply
propagates an error, it's easier to reason about where the error ends up being
handled? That's like saying you'd rather search for a needle in a haystack
than a needle on a platter.

With un-checked exception handling code, all I have to do is look for
"catches" in the call stack to my function. That code stands out and it's easy
to find. With Golang, I have to spend way more energy analyzing every caller
in the stack because they all touch my error.

~~~
sagichmal

        > So, your argument is that - because you actually see 
        > some code that simply propagates an error, it's easier 
        > to reason about where the error ends up being handled?
    

Not just where the error gets handled, but how it flows through the execution
path of your code -- but, yes.

    
    
        > That's like saying you'd rather search for a needle in
        > a haystack than a needle on a platter.
    

I'm sorry, but I don't see how this follows at all.

    
    
        > With Golang, I have to spend way more energy analyzing
        > every caller in the stack because they all touch my
        > error.
    

Spending time thinking about the error path for all of your code is time well
spent, because it's necessary if your code is going to function correctly.
Deferring consideration of failure results in dangerous (at best) and/or
broken (at worst) systems. I hope this is noncontroversial.

------
todd8
One goal, while writing programs, is to construct programs that operate
correctly (that is conform to the specifications that we have or imagine for
the finished product). Complex control flows make reasoning about correctness
difficult. Consider a simple loop:

    
    
      while x != y
        x = ...
        y = ...
      end while
      -- (A)
    

At point (A) we know that x == y. This allows us to reason about what happens
next in the program. Having a _break_ statement within the loop means that we
now have a more complex condition to consider at the end of the loop.

    
    
      while x != y
        x = ...
        if x == 0: break
        y = ...
      end while
      -- (B)
      

At point (B) we know that x == y or x == 0.

Once we introduce exceptions, we can end up outside the loop without knowing
anything about the state of x and y (unless we examine a lot more code,
possibly code quite remote from this little loop we are working on).

go's requirement to laboriously check and handle every function return may
make it easier to reason about x and y in our little example, but it comes at
the cost of extra boilerplate error checking everywhere. This too hurts our
ability to reason about the code we are writing by increasing verbosity, so
I'm not sure that go's approach will work out much better than some form of
exceptions mechanism.

However, I welcome this approach to language design, keeping the language as
simple as possible, but no simpler. Even if go error handling turns out in
practice to be roughly as good as C++'s approach, it will be a big win for go
because go ends up smaller, simpler, and easier to learn and master. It's
difficult to assemble a team of C++ programmers to work on complex systems
because it is so hard to find more than a handful of real C++ experts at a
time. I feel safer recommending the use of go to a typical programming team
than C++.

------
hornetblack
I find that a mustBeNil function is useful:

    
    
        func mustBeNil(err error) {
            if err != nil {
                panic(err)
                // or log.Fatal(err)
            }
        }
    

or

    
    
        func mustBeNil(err error, msg string) {
            if err != nil {
                log.Fatalf("%s: %s", err, msg)
            }
        }
    

;Edit: brackets

~~~
FiloSottile
Please, pull that `panic(err)` line. It is widely accepted that is bad
practice, and something you should _not_ do.

You panic when a program is expected to crash because of irrecoverable
corruption. Only.

~~~
sagichmal

        > You panic when a program is expected to crash because of 
        > irrecoverable corruption.
    

Or programmer error.

------
dscrd
Note that the other two hip languages of the moment, Rust and Swift, also do
not rely on exceptions for error management.

------
leaveyou
If you search for "err != nil" it returns more: 13,153

~~~
Jabbles
Does anyone know the total number of lines of Go on Github? Without knowing
that this number is meaningless.

------
martinml
On GitHub:

[https://github.com/search?q=%22if+err+%21%3D+nil%22+extensio...](https://github.com/search?q=%22if+err+%21%3D+nil%22+extension%3Ago)

