Naming errors in Go

Go programmers tend to use some (often not explicitly described) conventions when naming error variables and error types.

Here are various scenarios:

Declared error variables

Use prefixes Err or err for declared error variables, as in:

var ErrFormat = errors.New("zip: not a valid zip file")
var errLongName = errors.New("zip: FileHeader.Name too long")

Declared error types

Use the name Error or the suffix Error for declared error types, as in:

package url // import "net/url"

type Error struct {
    Op  string
    URL string
    Err error
}

func (e *Error) Error() string
func (e *Error) Temporary() bool
/* ... more declarations elided ... */

Or as in:

package os // import "os"

type LinkError struct {
    Op  string
    Old string
    New string
    Err error
}

func (e *LinkError) Error() string
func (e *LinkError) Unwrap() error

Use a linter such as predeclared to prevent this.

Local assignment

An assigned error variable, for the purpose of this post, is a variable that holds a returned error. For example, err is an assigned error variable below.

n, err := w.Write(p)
if err != nil {
    return err
}

For assigned error variables in function scope, name the error variable err.

Local assignment without shadowing

Sometimes you need to avoid shadowing an existing err. In such scenarios, name the second error variable err1, so that it doesn’t shadow the previous error variable named err.

For examples, consider the following piece of code and the gofmt source.

// Write data, then close the writer. If both Write and Close
// fail, we want the Write error to take precedence over the Close error.
_, err := w.Write(data)
_, err1 := w.Close()
if err != nil {
    return err
}
return err1

The names cerr or c (short for “close error”) are also okay here, instead of err1.

File scope assignment

If the assigned error variable is declared in file scope, use a more specific name, such as setupErr, to improve comprehension. For example:

package pax

var (
    once     sync.Once
    setupErr error
)

func setup() error { /* body elided */ }

func Create() error {
    once.Do(func() { setupErr = setup() })
    if setupErr != nil {
        return setupErr
    }
    // ... more code ...
}

func Read() error {
    once.Do(func() { setupErr = setup() })
    if setupErr != nil {
        return setupErr
    }
    // ... more code ...
}

Note that we do not want the names errSetup or setupError; these names conflict with scenarios 1 and 2, respectively.

Predeclared error type

Never shadow the predeclared identifier error which represents the built-in error interface.

f, error := os.Open("foo.txt") // BAD: DO NOT DO THIS.

errors.As variables

When using errors.As, name the target error variable <x>err where the placeholder <x> is the lowercased first character of the target error type.

For example, perr for PathError, as in:

var perr *fs.PathError
if errors.As(err, &perr) {
    log.Fatal(perr.Path)
}

Single character names such as e are also okay in this scenario, as in:

if err != nil {
    var e codeError
    if errors.As(err, &e) {
        os.Exit(e.code)
    } else {
        os.Exit(1)
    }
}

Do not reuse the variable name err.

Method receivers

Use one or two character-long names, as you typically would for a receiver name.

type MyError struct {}

func (m *MyError) Error() string

The names e or me are also appropriate for this example, but avoid names such as merr and myErr (both too lengthy) or err (confusing).

Named returns

In named returns, use the name err. Use documentation to discuss what the error represents.

func (c *Cbuf) Write(p []byte) (n int, err error)

A return name such as outerr is okay, too, in my opinion if you’re checking for an error in a deferred function or if you have several variables named err in the function body.