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

# Errors in the server SDK

> The server SDK's error model: every stable code, what it means, whether it is retryable, where it surfaces, and how API statuses map onto it.

Every failure the SDK reports, whether thrown as a `CuriousError`, carried in a decision result's `error`, or passed to `onError`, is a `CuriousErrorSummary`:

```ts theme={null}
type CuriousErrorSummary = {
  code: CuriousErrorCode;   // stable; branch on this
  status?: number;          // the HTTP status when there was a response
  retryable: boolean;       // whether the same request could succeed if retried
  requestId?: string;       // the server's X-Request-Id; quote it to support
  serverCode?: string;      // the API's `error` code when the failure was an API error body
  message: string;          // bounded and redacted: never a key, a user field, content or a raw body
};
```

`decide()` reports through the result; `identify()`, `declare()` and bad arguments throw; events report through `onError` and the flush summary.

## Codes

| Code | Meaning | Retryable | Fix |
| - | - | - | - |
| `invalid_configuration` | A constructor option is wrong: `apiKey` missing, `baseUrl` not `http(s)`, a non-positive number. | No | Fix the option. Thrown at construction. |
| `invalid_key_type` | `apiKey` is not a secret key (`curious_sk_…`). | No | Use a secret key on servers; the browser SDK takes publishable keys. |
| `invalid_input` | A required argument is missing (`element`, `userId`, `fallback`, `name`), or the API answered `400`, `413` or `422`. | No | Fix the field the message names. |
| `timeout` | The deadline passed, or the API answered `408`. | Yes | Serve the fallback. Raise `decideTimeoutMs` only if your budget allows. |
| `network_error` | The request never got a response. | Yes | Serve the fallback; events retry on their own. |
| `authentication_error` | `401`, or a `403` that is not about the origin. | No | Check the key; rotate if revoked. |
| `origin_not_allowed` | `403` `Origin not allowed for this key`. | No | Server code should not see this; it is a publishable-key error. |
| `rate_limited` | `429`. | Yes, after `Retry-After` | Batch events; serve the fallback on decide. |
| `undeclared_element` | `409` `No live experiment for element`. | No | Declare the element with a secret key first. |
| `server_error` | `5xx`. | Yes | Serve the fallback; events retry under their ids. |
| `protocol_error` | A non-JSON error response, `404`, `405`, or an unexpected status. | No | Check `baseUrl` and the path. |
| `content_shape_mismatch` | The response's content does not match the fallback's shape. | No | The declared format and the renderer have drifted; align the fallback with the control. |
| `queue_full` | `track()` found the queue at `eventQueueLimit`. | No | Flush more often, or raise the limit. |
| `event_rejected` | An event was dropped after the API rejected it, or attempts ran out. | No | Read the message; usually a `400` on the item. |
| `identity_conflict` | `identify()` got a `409`. | No | Resolve the link on your side. |
| `capability_unavailable` | The runtime lacks something the SDK needs. | No | Provide `fetch` through the option, or run on a supported runtime. |
| `mutation_drift` | `409` `idempotency_error`: the key was reused with a different request. | No | Use a new key for a new logical request. |
| `lock_timeout` | `409` `lock_timeout`: the user's identity changed while the decision was selecting. | Yes | The same request succeeds on a retry; `decide()` serves the fallback and reports it. |

A `409` that is none of the above (the active-arm limit) is `invalid_input`.

## Retry behaviour

The SDK never retries `decide()`. It retries event batches and `identify()` on `timeout`, `network_error`, `rate_limited` (after `Retry-After`) and `server_error`, up to `eventMaxAttempts`, always under the same ids. Everything else fails at once. The API-side rules are on [Idempotency](https://docs.trycurious.ai/api-reference/idempotency.md).

## Redaction

Messages are bounded to 300 characters and never contain the key, a user id, identifiers, attributes, event properties, returned content or a raw response body, so they are safe to log as they are. The `requestId` is what support needs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.