Error handling in Go

We disallow use of error packages — including the stdlib errors package — (enforced by a lint pass in CI) other than

We also require the use of errors.New or errors.Errorf over the use of fmt.Errorf, which also constructs an error type. This is to ensure that each error constructed by Sourcegraph is tagged with a stack depth and allows redaction of content within user-visible strings.

Use of errors.New

Use this function to create an error value with a static message.

var ErrSomethingWentWrong = errors.New("something went wrong")

Idiomatic error messages in Go should start with a lowercase letter and should contain no trailing punctuation.

Generally, errors of this class should be created as a constant at the highest level possible (e.g., an unexported package constant). Such errors should be exported if direct comparison of error values should be allowed by a user.

Idiomatically, error constant values should always have a name of the format ErrX (or errX if package-private) and types that can be used as error should have a name of the format XError.

Use of errors.Errorf

Use this function to create an error value with non-static message.

return errors.Errorf("user %d does not exist", userID)

The formatting directives are the same as fmt.Sprintf. The %w formatting directive special cases error values. Prefer to use errors.Wrap over this directive.

Use of errors.Wrap

Use this function to add additional data to an existing error value.

if err := myPkg.MyFunc(ctx); err != nil {
	return errors.Wrap(err, "myPkg.MyFunc")

Wrap all errors being returned from a method if that error did not originate in the same package (except for standard library packages like os or http which have very obvious and distinct error messages) or the same struct (unless its not used from multiple callers). This produces error messages with a usable stacktrace.

Use of errors.Is

Use this function to determine if any cause of a given error value is equal to a target error value.

for _, filename := range filenames {
	f, err := os.Open(filename)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			// skip missing files
		return err
	defer f.Close()

	// ...

Note: The second argument to this function should generally be error constant, or at the least a trivial (message-only) error value. Comparison of error values uses the == operator or the first error value’s Is(other error) bool method (if implemented). Comparing two error values of the same type but with different field values will generally not do what you want; use the HasType function instead.

Use of errors.IsAny

Use this function to determine if any cause of a given error is equal to at least one target error value.

if err := Some(ctx); err != nil {
	if errors.IsAny(err, context.DeadlineExceeded, context.Canceled) {
		// context error
	} else {
		// another type of error

As with the Is function, the second argument to this function should generally be an error constant.

Use of errors.HasType

Use this function to determine if a given error has a particular type.

type NotFoundError struct {
	ID string

func (e *NotFoundError) Error() string {
	return fmt.Sprintf("Object `%d` not found", e.ID)

// Note: We use a pointer here because the receiver of the
// target struct's Error method is a pointer. If the receiver
// was a value type, we'd use a value type here as well.
if errors.HasType(err, &NotFoundError{}) {
	// not found error
} else {
	// another type of error

This function should be preferred when comparing an error value with an error struct that may tag additional data (e.g., a failed opcode or the identifier of a missing object).

Note that using the Is function here will fail to match any error that does not have equivalent fields. In the example above, Is will only match error values where the value of the ID field is the zero value.

Use of errors.As

Use this function to safely cast a given error value to a particular type. This should be preferred over using Go language-level type casing of an error value (e.g., err.(*MyType)), which fails to unwrap errors.

type OpError struct {
	Op string

func (e OpError) Error() string {
	return fmt.Sprintf("Oops because of %s", e.Op)

// Note: We use a value type here because the receiver of the
// target struct's Error method is a value type. If the receiver
// was a pointer, we'd use a pointer here as well.
var e OpError

if errors.As(err, &e) {
	switch e.Op {
		// ...
} else {
	// ...

This function can also be used with interface values.

func isRetryable(err error) bool {
	var e interface { Retryable() bool }

	if errors.As(err, &e) {
		return e.Retryable()

	return false

Use of errors.Cause

Use this function to remove all layers from a given error value and return only the root cause. This function should be used rarely as the Is, HasType, and As functions cover the most common functionality.

fmt.Printf("Root error: %s\n", errors.Cause(err))