Errors handling in Golang

Go does not have an Exception handling model as most of the main stream languages. However, it uses the error interface type as the return type for any error that is going to be returned from a function or method:

type error interface {
    Error() string
}

It is an interface type. An error variable represents any value that can describe itself as a string. The most commonly-used error implementation is in the errors package.

It can be instaciated in the following way:

func DivideBy(divider float64) (float64, error) {
    if divider <= 0 {
        return 0, errors.New("Divider cannot be zero or negative number.")
    }
    // implementation
}

The errors.New functions constructs an exported type errorString that implements the Error interface:

// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package errors implements functions to manipulate errors.
package errors

// New returns an error that formats as the given text.
func New(text string) error {
	return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
	s string
}

func (e *errorString) Error() string {
	return e.s
}

It is pretty straighforward to implement your own error type that has additional data.

The error model in Golang does not provide a way to find out, which function returned the error. We should be aware and log the errors very carefully in order to understand where this error occurred.

Fortunately, the Golang runtime provides a set of functions that we can use to generate a stacktrace that we can trace down easily.

In the following paragraphs, we will explore the Planatir stacktrace package that does this for us.

Getting Started

The package captures a strategic places along the call stack and attaches relevant contextual information like messages and variables. It is keeping stack traces compact and maximally useful.

Installation

In order to use the package, we should install it first by using the well known go get command:

$ go get github.com/palantir/stacktrace

Usage

The package provides a various functions to propagate and generate that contextual information.

Error propagation

stacktrace.Propagate function replaces the usage of fmt.Errorf function. It wraps an error to include line number information. This is going to be your most common stacktrace call.

db, err := sql.Open("postgres", conninfo)
if err != nil {
   return stacktrace.Propagate(err, "Failed to connect %v", conninfo)
}

Creating errors

stacktrace.NewError creates a new error that includes line number information:

if amount <= 0 {
    return stacktrace.NewError("Expected amount %v to be positive number", arg)
}
Error Codes

Sometimes it’s useful to propagate an error code while unwinding the stack. For instance, a RESTful API may use the error code to set the HTTP status code. The type stacktrace.ErrorCode is used to name the set of error codes relevant to your application:

const (
    ConnectionTimeout = stacktrace.ErrorCode(iota)
		ConnectionLost
)

The value stacktrace.NoCode is equal to math.MaxUint16, so avoid using that. NoCode is the default value of errors that does not have explicitly set error code.

You can use stacktrace.PropagateWithCode and stacktrace.NewErrorWithCode to instaciated an error that has specific code:

db, err := sql.Open("postgres", conninfo)
if err != nil {
   return stacktrace.PropagateWithCode(err, ConnectionTimeout, "Failed to connect %v", conninfo)
}

You can extract the error code from the error by using stacktrace.GetCode function:

data, err := fetch()
if err != nil {
  code := stacktrace.GetCode(err)
	if code == ConnectionTimeout {
	   return nil
	}
}

Verdict

The stacktrace package is very pleasant and easy to use. It comforms the Golang idiomatic way of handling errors and provides us with additional contextual information about the file and the line where the error occurred.

Tags# , ,