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

# Error handling

> HTTP status codes, error shapes, and how to handle partial failures

## HTTP status codes

| Status                      | Meaning                                                                                     | Retryable                         |
| --------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------- |
| `400 Bad Request`           | Invalid request parameters (e.g. missing `name`)                                            | No                                |
| `401 Unauthorized`          | API key is missing, invalid, or revoked                                                     | No                                |
| `404 Not Found`             | The requested resource does not exist                                                       | No                                |
| `409 Conflict`              | Concurrency (`STALE_WRITE`) or unique-constraint (`CONFLICT`) conflict on a business upsert | No                                |
| `429 Too Many Requests`     | Rate limit exceeded                                                                         | Yes — after `Retry-After` seconds |
| `500 Internal Server Error` | Unexpected server error                                                                     | Yes — with backoff                |
| `502 Bad Gateway`           | Every data source in a search failed (`meta.status` is `error`)                             | Yes                               |

All error responses follow this shape — a human-readable `error` and a machine-readable `code`:

```json theme={null}
{ "error": "Human-readable error message", "code": "VALIDATION_ERROR" }
```

### Top-level error codes

Every non-2xx response includes a `code`. These are distinct from the per-source
codes inside a successful response's `errors` object (documented below).

| Code               | Typical status | Meaning                                                        |
| ------------------ | -------------- | -------------------------------------------------------------- |
| `MISSING_API_KEY`  | 401            | No API key supplied                                            |
| `INVALID_API_KEY`  | 401            | API key not recognized                                         |
| `REVOKED_API_KEY`  | 401            | API key has been revoked                                       |
| `EXPIRED_API_KEY`  | 401            | API key has expired                                            |
| `RATE_LIMITED`     | 429            | Rate limit exceeded — retry after `Retry-After` seconds        |
| `VALIDATION_ERROR` | 400            | A request parameter was missing or invalid                     |
| `INVALID_JSON`     | 400            | Request body was not valid JSON                                |
| `NOT_FOUND`        | 404            | The referenced resource does not exist or isn't yours          |
| `STALE_WRITE`      | 409            | Optimistic-concurrency conflict (`expectedUpdatedAt` mismatch) |
| `CONFLICT`         | 409            | Unique-constraint conflict (registration number / external id) |
| `SERVER_ERROR`     | 500            | Unexpected server error — retry with backoff                   |

## Error codes in search responses

When a data source encounters an error mid-search, the `errors` object in the response body contains structured error details. Keys are the data-source names — `businessRegistry`, `regulatoryRegistry`, `sanctionsScreening`, `websiteAnalysis` — plus `general` for a search-wide failure that isn't tied to a single source (for example, the search timing out):

```json theme={null}
{
  "errors": {
    "businessRegistry": {
      "code": "TIMEOUT",
      "message": "Business registry request timed out after 40 seconds",
      "retryable": true
    }
  }
}
```

| Code             | Description                                     |
| ---------------- | ----------------------------------------------- |
| `TIMEOUT`        | The data source did not respond in time         |
| `RATE_LIMITED`   | The upstream service rate-limited the request   |
| `NOT_FOUND`      | No records found for this query                 |
| `INVALID_INPUT`  | The query could not be processed by this source |
| `UPSTREAM_ERROR` | The upstream service returned an error          |
| `PARSE_ERROR`    | The upstream response could not be parsed       |
| `AUTH_ERROR`     | Authentication with the upstream service failed |
| `UNAVAILABLE`    | The upstream service is temporarily down        |
| `UNKNOWN`        | An unexpected error occurred                    |

When `retryable` is `true`, re-running the search may succeed.

## Partial failures

A search with some errors still returns all data from the sources that succeeded. The top-level `status` will be `completed-with-errors` rather than `completed`.

```json theme={null}
{
  "meta": {
    "status": "completed-with-errors",
    ...
  },
  "businesses": [ ... ],  // succeeded
  "regulatory": { ... },  // succeeded
  "errors": {
    "sanctionsScreening": {
      "code": "UNAVAILABLE",
      "message": "Sanctions service temporarily unavailable",
      "retryable": true
    }
  }
}
```

<Note>
  Always check both `meta.status` and the `errors` object. A
  `completed-with-errors` response still contains valid data — only the listed
  sources failed.
</Note>
