> ## Documentation Index
> Fetch the complete documentation index at: https://s2.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Codes

> Error codes returned by the S2 API, including retryability and structured error bodies.

Conventional HTTP status codes are returned to indicate the success or failure of an API request.

Certain errors are retryable, meaning that re-issuing the same request after a delay could lead to a successful result. Exponential backoff is recommended.

<Note>
  In the context of an [S2S](/api/protocol#sessions) `append` or `read` session, an error that occurs after a `200 OK` status will be a "terminal message" on the response stream. However, pre-flight errors are sent normally.
</Note>

## Standard Errors

A standard error body has the following JSON form:

```json theme={null}
{
  "code": "brief_reason",
  "message": "more information about the error"
}
```

| Code                      | HTTP Status | Retryable | Side Effect |
| ------------------------- | ----------- | --------- | ----------- |
| `bad_header`              | 400         | No        | No          |
| `bad_path`                | 400         | No        | No          |
| `bad_query`               | 400         | No        | No          |
| `bad_json`                | 400         | No        | No          |
| `bad_proto`               | 400         | No        | No          |
| `bad_frame`               | 400         | No        | No          |
| `decryption_failed`       | 400         | No        | No          |
| `authn`                   | 401         | No        | No          |
| `invalid`                 | 422         | No        | No          |
| `permission_denied`       | 403         | No        | No          |
| `quota_exhausted`         | 403         | No        | No          |
| `basin_not_found`         | 404         | No        | No          |
| `stream_not_found`        | 404         | No        | No          |
| `access_token_not_found`  | 404         | No        | No          |
| `request_timeout`         | 408         | Yes       | Maybe       |
| `resource_already_exists` | 409         | No        | No          |
| `basin_deletion_pending`  | 409         | No        | No          |
| `stream_deletion_pending` | 409         | No        | No          |
| `transaction_conflict`    | 409         | Yes       | No          |
| `rate_limited`            | 429         | Yes       | No          |
| `client_hangup`           | 499         | No        | Maybe       |
| `other`                   | 500         | Yes       | Maybe       |
| `storage`                 | 500         | Yes       | Maybe       |
| `not_implemented`         | 501         | No        | No          |
| `hot_server`              | 502         | Yes       | No          |
| `unavailable`             | 503         | Yes       | Maybe       |
| `upstream_timeout`        | 504         | Yes       | Maybe       |

<Tip>
  Requests that fail with `404` or `409` will succeed on a future attempt once the underlying condition changes (e.g., resource is created, or deletion completes).
</Tip>

## Structured Errors

### Append Condition Failed (412)

See [concurrency control](/concepts/concurrency-control) for more context.

<Tabs>
  <Tab title="Fencing Token Mismatch">
    ```json theme={null}
    {
      "fencing_token_mismatch": "the-actual-fencing-token"
    }
    ```

    The current fencing token did not match the expected fencing token, which is returned.
  </Tab>

  <Tab title="Sequence Number Mismatch">
    ```json theme={null}
    {
      "seq_num_mismatch": 42
    }
    ```

    The expected sequence number did not match the assignable sequence number, which is returned.
  </Tab>
</Tabs>

### Read Range Not Satisfiable (416)

<Callout icon="glasses">
  `416` without a `Range` header? Unconventional, but the shoe fits.

  See [when reads return `416`](/api/records/read#when-reads-return-416).
</Callout>

```json theme={null}
{
  "tail": {
    "seq_num": 42,
    "timestamp": 1767996832000
  }
}
```

The `tail` field indicates the current end position of the stream.
