Error interfaces

February 20, 2025

On Monday I talked about error structs. Let’s continue that theme, with error interfaces.

To recap the problem: You want to the caller of your function to be able to extract context from an error, without parsing error strings. Or maybe you even want to include context that isn’t part of the error string at all! Monday’s solution to this problem had us using custom structs.

A similar, but often more flexible approach, is to use an error interface.

Let’s reimagine net’s AddrError type, using an interface:

type AddrError interface {
	Err() string
	Addr() string
}

Now we can check it using a similar approach as we would for an error struct:

err := doSomething()
var addrErr interface {
	Err() string
	Addr() string
}
if errors.As(err, &addrErr) {
	fmt.Println("The error occurred when trying to access %s", addrErr.Addr())
}

What’s missing from this short example, is the actual implementation of the error type. We just have an interface. An interface only defines a method set–not the actual type that implements the interface.

And this is where the flexibility of this approach comes in. You can choose to implement your error interface any way you wish.

Here’s a simple struct implementation:

type addrError struct {
	err string
	addr string
}

func (e addrError) Err() string {
	return e.err
}

func (e addrError) Addr() string {
	return e.addr
}

But you could also create alternative implementations:

type addresslessError string

func (e addresslessError) Err() string {
	return string(e)
}

func (addresslessError) Addr() string {
	return ""
}

If you only ever have one implementation of a specific error type, an error struct is likely a good approach. But if you want multiple implementations, then consider an interface.

Here’s an example of a simple error interface I often use for web applications:

interface {
	HTTPStatus() int
}

Notice I didn’t name the interface. That’s not striclty necessary, and often provides no real value. I typically create a helper function along with such a type:

// HTTPStatus returns the HTTP status contained in err. If err is nil, 0 is
// returned. If the error does not contain an HTTP status, 500 is returned.
func HTTPStatus(err error) int {
	if err == nil {
		return 0
	}
	var httpStatusErr interface { HTTPStatus() int }
	if errors.As(err, &httpStatusErr) {
		return httpStatusErr.HTTPStatus()
	}
	return 500
}

I’ll have an HTTP middleware call that HTTPStatus() method on an error, to determine which HTTP status to return to the client.

And then throughout my application, I can create any number of specific error types that implement that HTTPStatus() method, and they’ll play nicely with my error handling middleware.


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe