Fun (or Not) With Golang Errors

6 error-related problems I’m trying to solve

Travis Reeder
Nov 25, 2020 · 5 min read
Dark forest
Dark forest
Photo by Geran de Klerk on Unsplash.

I love Go and have been writing code with it for nearly a decade, but dealing with errors has always been a struggle. My previous company was one of the first companies in the world to use Golang in production and I’ve battled with how best to handle errors since day one. Somehow things just never felt right, so lately I started really experimenting with them to make using them feel OK.

These are the problems I wanted to solve:

  • How do I get a stack trace where the error occurred?
  • When and where should I write log output for the errors?
  • How do I differentiate between an error I want to show a user and what I want to show up in the logs?
  • How do I add context so I can use structured logging when I want to log the error?
  • How can I reduce the amount of code dedicated to error handling and logging?
  • How do I follow common Go best practices such as the ones suggested by Dave Cheney and accomplish the above?

I’ve tried everything at least once, just to see how it feels.

For instance, logging at the deepest point so I can get a stack trace in my logs and the proper context, and then returning the error. Logging libraries like Uber’s Zap will print the stack trace and structured content:

This always felt a bit ugly to me, as it requires an extra line for logging every time you get an error. This also means passing the around by either explicitly adding it as a parameter to every function or using the context to pass it around (the cleaner way). Some would argue adding it to the context isn’t a good idea either.

And you inevitably log the same error more than once if you aren’t careful and check that it wasn’t already logged deeper down. So you end up with things like this:

You can see where this is going, and it’s not a happy place!

When you’re working on an API (which is probably most of us), things can get even worse with code like this:

Now it’s getting really, really ugly. And all this extra no-good code is all over the place on every error check (and in Go, you have error checks everywhere).

So what’s a poor Go developer to do?

My Latest Go Error Experiments

I think I’ve finally figured out a fairly elegant way to deal with everything above while keeping the error-handling code to a minimum. A revelation in my own mind.

I have a few GitHub repos that I use for experimentation, and I’ll be referencing one in particular in this section called gotils.

Step 1: Add context to the context

Instead of adding context to your (structured fields), add them to the Go context instead:

The nice thing about this is that you can use these contextual fields anywhere — not just for your logging library. It’s just a basic map of entries. And adding context to the context seems like a proper fit.

Step 2: Add context and a stack trace to errors

Instead of trying to log deep down so we can get a proper stack trace and context, let’s add the stack and the context to the errors we return:

The function wraps your error and adds the current stack along with the context field map and returns a new error with that information.

Step 3: Log and return the error response to the user at the entry point

If this is an API and we got an error, then we’d only deal with the error at the entry point (i.e. the HTTP handler):

So now we’ve removed all of the ugly error-handling and logging code from the innards of the application, and we only do it once at the edge.

Reduce logging and error handling even more

To make this type of handling even better, try using an that lets your HTTP handlers return an error. This means we don’t need to deal with errors in every handler — just one place that wraps all the other handlers. This looks like the following:

Then you have one place in your entire application that deals with logging and returning the errors.

You can see a full example of the that deals with and whatnot.

User-specific errors to hide internal errors from users and return informative messages

Oftentimes, you don’t want your users to see an error that occurred from within your program (e.g. a database error). You probably don’t want to send a back to your user, but you probably want to log it or deal with it somehow.

One way to handle it is to just return generic errors, like a 500 Internal Server Error if an error makes its way out, but that’s very rigid and you can’t return a detailed response about what exactly went wrong to the user. For instance, if it was just invalid input, you’d want to return a message like “field X is invalid.”

So we need a way to differentiate error messages for internal use and error messages for users. For this, I made a interface:

It’s used like this:

Then when it’s time to return your response:

There’s also a if you want to return specific HTTP status codes.

How to use these with your logging library

Your logging library won’t know how to extract the stack or the fields automatically, but luckily, this is pretty easy. Even better, you might only have one line in your entire app that actually uses your logging library!

If you try the above with your own code, just be sure to have a couple of methods to get the context fields and stack from the error. has and interfaces that provide these methods that you can copy or just use directly if you’d like:

Call those to get the right info and pass it along to your logging library.

An example of this in the gcputils repo that will log this all in the proper format for Google Cloud logging with a simple call to:

Conclusion

These things have allowed me to clean up a lot of messy code that always bugged me, but I was too busy (or lazy) to figure out a better way. Until recently, that is, when I just had enough and dedicated time to experiment with logging and error handling.

Try some of the concepts discussed here and see what you think. I’d love to hear your feedback or learn how you are dealing with these things.

“To err is human; to forgive, divine.” — Alexander Pope

Better Programming

Advice for programmers.

Sign up for The Best of Better Programming

By Better Programming

A weekly newsletter sent every Friday with the best articles we published that week. Code tutorials, advice, career opportunities, and more! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Thanks to Zack Shapiro

Travis Reeder

Written by

Founder, CTO at GoChain - Building and breaking things

Better Programming

Advice for programmers.

Travis Reeder

Written by

Founder, CTO at GoChain - Building and breaking things

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store