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.