Go errors

Errors in Go

Knowing which method to use in the errors package still confuses me.

Say we have a custom error:

func NewConnectionError(statusCode int) error {
	return &ConnError{
		StatusCode: statusCode,
	}
}

type ConnError struct {
	StatusCode int
}

func (e *ConnError) Error() string {
	return "ConnError: there was a problem connecting"
}

We want to see if an error is of type ConnError.

func TestTryConnect(t *testing.T) {
	err := NewConnectionError(1)
	err2 := NewConnectionError(2)

	wrappedErr := fmt.Errorf("wrapped: %w", err)

	var ce *ConnError
    assert.True(t, errors.As(wrappedErr, &ce))
	assert.Equal(t, 1, ce.StatusCode)

    assert.True(t, errors.Is(wrappedErr, err))
	assert.False(t, errors.Is(wrappedErr, err2))
}

We have proven that the wrapped error is of the ConnError type by using errors.As(...). It also sets ce to point to the error in the chain that is of type ConnError.

We have also proven that the wrapped error contains the same error initialized with NewConnectionError(1). The usage in this test is not particularly helpful though.

Some packages, however, export initialized errors for easy comparison. Take for example, context.Canceled (source), in the Go standard library.

In the following code, we can simply use errors.Is to figure out if the context was cancelled.

func TestThis(t *testing.T) {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)

	cancel()

	err := ctx.Err()
	assert.True(t, errors.Is(err, context.Canceled))
}

There could have been a custom error type for this, but there would be no additional information to add to the custom error type.

© Christopher Docuyanan