On the subject of error handling in Go

On the subject of error handling in Go

One of the strangest things Go developers can do is to use a specific error type rather than an interface as the return type of a function. If you do, remember this is a very bad idea with unpredictable results.

Let's take a look at this block of code:

type myError struct{}

func (c *myError) Error() string {
	return "Unexpected Error."

func run() ([]byte, *myError) {
	return nil, nil

func main() {
	var err error
	if _, err = fail(); err != nil {
		log.Fatal("We are failed")
	log.Println("Everything is OK")


If we run this program, we suddenly get the error "We are failed". Why does this happen? The reason is that the run function uses a particular implementation of the error and not the interface. In this case the nil pointer of type myError is stored inside the variable err. And this is not the same as an empty interface value of type err.

Error handling is more of an engineering thought on a macro level. So what does "error handling" mean? An error is stopped by the error-handling function, the error is logged with the full context, and the error is checked for severity. Based on the severity and recoverability, a decision is made whether to recover, continue, or terminate the script.

One problem is that not all functions can handle the error. Simply not all functions keep an error log. What happens when an error is passed back up the call stack and cannot be handled by the function that received it? The error must be wrapped in context so that the function that ultimately handles it can do so properly.

There are two options for wrapping additional context around the error. You can use Dave Cheney's third-party errors package, or you can use the standard library support found in the errors and fmt packages. Whatever I decide, it is important to annotate errors for sufficient context to help identify and fix problems. Both at runtime and afterwards.


An example of using a third party package:

package main
import (

type MainError struct {
	State int
func (c *MainError) Error() string {
	return fmt.Sprintf("Main Error, State: %d", c.State)
func main() {
	if err := first(10); err != nil {
		switch v := errors.Cause(err).(type) {
		case *MainError:
			fmt.Println("Custom Main Error:", v.State)
			fmt.Println("Default Error")
	fmt.Printf("%v\n", err)
func first(i int) error {
	if err := second(i); err != nil {
		return errors.Wrapf(err, "secondCall(%d)", i)
	return nil
func second(i int) error {
	return &MainError{99}


There will be following output:


Custom Main Error: 99

second(10): Main Error, State: 99


What I like about this package is the errors.Wrap and errors.Cause functions. They make the code more readable.


If we were writing code without using the library, we would need to create these functions ourselves:


func Cause(err error) error {
root := err
for {
if err = errors.Unwrap(root); err == nil {
return root
root = err


Thus, it is very important to approach the question of error handling with great care. It is necessary to pass the full context to functions and pay attention to the return type.

Popular Posts

My most popular posts

Maximum productivity on remote job

Maximum productivity on remote job

I started my own business and intentionally did my best to work from anywhere in the world. Sometimes I sit with my office with a large 27-inch monitor in my apartment in Cheboksary. Sometimes I’m in the office or in some cafe in another city.

Hello! I am Sergey Emelyanov and I am hardworker
Business PHP

Hello! I am Sergey Emelyanov and I am hardworker

I am a programmer. I am an entrepreneur in my heart. I started making money from the age of 11, in the harsh 90s, handing over glassware to a local store and exchanging it for sweets. I earned so much that was enough for various snacks.

Hire Professional CRM developer for $25 per hour

I will make time for your project. Knowledge of Vtiger CRM, SuiteCRM, Laravel, and Vue.js. I offer cooperation options that will help you take advantage of external experience, optimize costs and reduce risks. Full transparency of all stages of work and accounting for time costs. Pay only development working hours after accepting the task. Accept PayPal and Payoneer payment systems. How to hire professional developer? Just fill in the form