---
title: A. Errors
---

## Twirp Error Handling

The "standard" method of returning errors in Twirp is to use Twirp response codes:
```go
return nil, twirp.NewError(twirp.NotFound, "not found")
```
However, we have found several flaws with this method:
1. It relies on callers anticipating Twirp errors and checking their response codes, rather than following
the Go-standard practice of `if resp, err := DoSomething(); err != nil { return err }`
1. It has the potential to mislead your error request metrics by adding a class of 4xx errors
1. If you want to pass more information than just an error code, you have to either return *both* a response
body and an error (which is dangerous as callers can not be relied on to check the response body on errored
requests), or embed metadata directly in the error, which is fragile as it is not structured like our protobuf is.

## Custom Error Codes

We have solved these problems by borrowing a pattern from GraphQL: custom error codes embedded in response objects.

```
message CreateThingResponse {
    Thing created_thing = 1;
    CreateThingError error = 2;
}

message CreateThingError {
    CreateThingErrorCode code = 1;
}

enum CreateThingErrorCode {
    CREATE_THING_NO_ERROR = 0;
    CREATE_THING_FORBIDDEN = 1;
    CREATE_THING_TOO_MANY_THINGS = 2;
}
```

Some things to take note of here:

1. We do not directly embed the `CreateThingErrorCode` as a field in `CreateThingResponse` –
instead, we wrap it in its own `CreateThingError` type. This allows us to set `response.Error = nil`,
and also has another benefit to be explained later.
2. We always set the "zero value" as `*_NO_ERROR`. This is to ensure that we are always *purposeful* in our error setting.
3. For our enums, we preface each enum name with `CREATE_THING_`. This is critical because Twirp/Protobuf enums
are globally namespaced, and so we want to make sure there is no overlap between different error code names.

## Adding Metadata

This approach does add some extra boilerplate to our Twirp/Protobuf definitions, but it really shines when
we start to add custom metadata to our error responses. For example, for our `TOO_MANY_THINGS`
error, perhaps our clients want the ability to display a message such as:

> Could not Create Thing! The maximum number of Things is {Number}.

In order to pass that `{Number}` back to clients, we can add some metadata to the CreateThingError message:

```diff
message CreateThingError {
    CreateThingErrorCode code = 1;
+   int max_things = 2; // metadata only populated for TOO_MANY_THINGS error code.
}
```

## Caller Handling

As a caller of a Twirp service designed in this way, you should follow a pattern of checking both the standard
`err != nil` as well as checking the embedded error code. For example:

```go
resp, err := s.ThingClient.CreateThing(ctx, req)
if err != nil {
    return nil, err
}
if resp.GetError() != nil && resp.GetError().GetCode() != rpc.CreateThingErrorCode_NO_ERROR {
    switch resp.GetError().GetCode() {
    case rpc.CreateThingErrorCode_FORBIDDEN:
        // custom handler
    case rpc.CreateThingErrorCode_TOO_MANY_THINGS:
        // custom handler
    default:
        return nil, fmt.Errorf("unexpected Twirp error code %v", resp.GetError().GetCode())
    }
}
```