Error codes reference
Every error from the Spanlight API is a JSON document with the media type application/problem+json, as defined by RFC 9457. This page explains how to read one and lists the codes you meet most often. The API conventions page has the full catalogue, including the codes of single features such as prompts, datasets and experiments.
The gateway under /gw/ answers in the format of the provider API it imitates, not in problem+json. See Gateway errors for those.
The shape of an error
Section titled “The shape of an error”{ "type": "about:blank", "title": "Unprocessable Entity", "status": 422, "detail": "The request is invalid.", "code": "VALIDATION_ERROR", "request_id": "0b7c1f0e5d2a4c6e8f1a3b5c7d9e2f40", "errors": [{ "field": "retention_days", "message": "Input should be less than or equal to 90" }]}| Field | Meaning |
|---|---|
status |
The HTTP status code, repeated in the body. |
title |
The standard name of the status. |
detail |
A sentence for people. Do not parse it, because the wording can change. |
code |
A stable name for the error. Branch on this in your code. |
request_id |
The ID of the request. It is also in the X-Request-ID response header. Quote it when you report a problem. |
errors |
Only on VALIDATION_ERROR. One entry for each field, with its dotted path and a message. |
Some errors add more fields. A response can also carry a Retry-After header, in whole seconds. RATE_LIMITED and SERVICE_UNAVAILABLE always do.
How to read an error
Section titled “How to read an error”- Look at the
statusfirst. A4xxmeans the request is wrong or not allowed. Retrying it unchanged will not help, except for429andIDEMPOTENCY_IN_PROGRESS. A5xxmeans the server could not finish the request. AGETis safe to retry. A write may have been applied in part, so retry it only if the route accepts anIdempotency-Key. Send the same key again. Spanlight never keeps the answer to a5xxunder a key, so the retry runs. Today the routes that accept the header create exports, run playground prompts, start experiment runs and send a test to an alert channel. - Read the
codeto find the cause in the tables below. - Read
detailfor the specifics, such as which setting to change or which field is wrong. - For
VALIDATION_ERROR, read each entry oferrors.
Two things behave in a way you might not expect. A resource that exists but that you may not see answers 404 NOT_FOUND, not 403. And a status that has no specific code uses a generic one: METHOD_NOT_ALLOWED for 405, CONFLICT for 409 and ERROR for anything else.
Request and validation errors
Section titled “Request and validation errors”| Code | Status | Meaning | What to do |
|---|---|---|---|
BAD_REQUEST |
400 | The request could not be understood. On ingestion this is a body that could not be decompressed or was not fully sent. | Check the Content-Encoding header and send the whole body. |
INVALID_JSON |
400 | The ingestion body is not valid JSON. | Fix the JSON. |
INVALID_OTLP |
400 | The OTLP body could not be decoded. | Send OTLP JSON or protobuf with the matching Content-Type. |
VALIDATION_ERROR |
422 | A field is missing, has the wrong type or is out of range. For a refused cost CSV, each entry of errors has field file with the row and column at fault (up to 20), and nothing was stored. |
Read errors and fix each field, or each row of the file. |
PAYLOAD_TOO_LARGE |
413 | The body is over its size limit, or an ingestion batch has too many spans. A dataset file or a billing source’s cost CSV is over 5 MB. | Send smaller bodies and batches, or split the file. See the Limits reference. |
UNSUPPORTED_MEDIA_TYPE |
415 | The Content-Type or Content-Encoding is not supported. A billing source’s cost CSV was not sent as multipart/form-data. |
Use application/json or application/x-protobuf, and gzip or deflate encoding. Send a CSV upload as multipart/form-data with the file in a field named file. |
SEARCH_TOO_BROAD |
422 | A trace search (q) matched too much to finish within 5 seconds, usually a common word over a long time range. |
Narrow the time range or search for more specific text. |
CONFIRMATION_MISMATCH |
422 | A deletion’s typed confirm is not the slug of what it deletes. |
Send the exact slug. |
IDEMPOTENCY_MISMATCH |
422 | The Idempotency-Key was first used for a different request. |
Send a new key for a different request. |
IDEMPOTENCY_IN_PROGRESS |
409 | The first request with this Idempotency-Key has not answered yet. |
Wait a moment and send the same request again. |
Authentication and permission errors
Section titled “Authentication and permission errors”| Code | Status | Meaning | What to do |
|---|---|---|---|
UNAUTHORIZED |
401 | No valid session or credential came with the request. | Sign in, or send Authorization: Bearer <key or token>. |
INVALID_CREDENTIALS |
401 | The email or password is wrong. | Check both. Repeated failures are throttled. |
KEY_EXPIRED |
401 | The API key is past its expiry. | Create a new key. |
TOKEN_EXPIRED |
401 | The personal access token is past its expiry. | Create a new token. |
FORBIDDEN |
403 | You are signed in, but your role does not allow this. | Ask an admin or owner. See the roles reference. |
KEY_SCOPE |
403 | The API key does not have the scope this route needs. | Create a key with the scope. A key cannot change its scopes. |
TOKEN_SCOPE |
403 | A read-only access token tried to change something. | Use a write token. |
SESSION_REQUIRED |
403 | The route needs a signed-in browser session, not a token. | Use the dashboard. |
TWO_FACTOR_REQUIRED |
403 | The organization requires two-factor authentication, and you have not turned it on. | Turn it on in your account settings. |
CSRF_FAILED |
403 | The CSRF token is missing or wrong. | Reload the dashboard. Scripts should use a bearer token, which skips this check. |
ORIGIN_NOT_ALLOWED |
403 | The Origin header is missing or not on the allowed list. |
Call from the dashboard origin, or use a bearer token. |
NOT_FOUND |
404 | The resource does not exist, or you cannot see it. | Check the ID and your membership. |
Conflicts and server errors
Section titled “Conflicts and server errors”| Code | Status | Meaning | What to do |
|---|---|---|---|
NOT_CONFIGURED |
409 | The request needs an optional integration that the operator has not set up. detail names the setting. For example, a billing source that fetches from a provider needs CREDENTIALS_KEYS, because its admin key is stored encrypted, and refreshing one needs BILLING_SOURCES_ENABLED on. |
Set it. See Configuration. A billing source uploaded as CSV holds no key and works without it. |
LAST_OWNER |
409 | The change would leave the organization without an owner. | Make someone else an owner first. |
EMAIL_TAKEN |
409 | An account with this email already exists. | Sign in instead. |
SLUG_UNAVAILABLE |
409 | Spanlight could not find a free slug for the name. | Try another name. |
BILLING_SOURCE_NAME_TAKEN |
409 | Another billing source of the organization has this name. | Choose another name. |
BILLING_SOURCE_PROJECT_TAKEN |
409 | A project you mapped is already compared with another billing source of the same provider. detail names that source, and errors points at project_ids. |
Remove the project from the other source first, or leave it out. |
BILLING_SOURCE_DISABLED |
409 | You asked to refresh a billing source that is turned off. | Turn the source on, then refresh it. Its report stays readable while it is off. |
BILLING_FETCH_IN_PROGRESS |
409 | A fetch or upload of the billing source is still running. | Wait for it to finish, then try again. A fetch still running after 15 minutes no longer blocks a new one. |
RATE_LIMITED |
429 | You sent too many requests. The Retry-After header says how long to wait. A billing source can be refreshed once every 5 minutes, with bursts of two. |
Wait that long, then retry. Send larger batches in fewer requests. |
INTERNAL_ERROR |
500 | An unexpected error on the server. | Retry a GET once. Retry a write only with the same Idempotency-Key, on a route that accepts one (see step 1 of How to read an error). If it persists, report it with the request_id. |
SERVICE_UNAVAILABLE |
503 | The database could not take the request in time. Retry-After is 5 seconds. |
Retry a GET after the delay. Retry a write only with the same Idempotency-Key, on a route that accepts one (see step 1 of How to read an error). The Python SDK retries trace ingestion by itself. |
Review and replay errors
Section titled “Review and replay errors”| Code | Status | Meaning | What to do |
|---|---|---|---|
QUEUE_LACKS_FAILURE_CATEGORY |
422 | The queue of a review policy does not ask for the failure_category score, or that config is not categorical. |
Add the categorical failure_category config to the queue, or pick a queue that has it. See Daily review. |
NOTHING_SELECTED |
422 | Every reason of the review policy is off and its random share is 0, so it would never pick a trace. | Turn on a reason or set a random share. |
DAY_NOT_COMPLETE |
422 | A manual selection asked for today or a later day. | Pick a finished UTC day. |
DAY_TOO_OLD |
422 | A manual selection asked for a day more than 7 days back. | Pick a day from yesterday back to 7 days. |
ALREADY_SELECTED |
409 | The day has been selected already, by the nightly run or by hand. | Nothing to do: its items are in the queue. |
QUEUE_IN_USE |
409 | A review policy fills the queue you tried to delete. | Delete the policy, or point it at another queue, first. |
REPLAY_BUDGET_EXCEEDED |
409 | The project’s monthly replay budget cannot cover the replay, so nothing was sent. The body adds spent_usd, reserved_usd, budget_usd and month_start. |
Wait for the next UTC month, or ask an operator to raise REPLAY_MONTHLY_BUDGET_USD. |
A replay that cannot be sent as it was stored answers 422 with one of eight codes: REPLAY_NOT_LLM, REPLAY_NO_PAYLOAD, REPLAY_PAYLOAD_TRUNCATED, REPLAY_PAYLOAD_REDACTED, REPLAY_UNSUPPORTED_INPUT, REPLAY_PROVIDER_MISMATCH, REPLAY_MODEL_UNPRICED and REPLAY_BODY_TOO_LARGE. When a replay is refused says what each means and what to do. A replay with REPLAY_MONTHLY_BUDGET_USD set to 0 answers 409 NOT_CONFIGURED.
Related pages
Section titled “Related pages”- API conventions: the full catalogue of codes and the rules behind them.
- Limits reference: the numbers behind
PAYLOAD_TOO_LARGEandRATE_LIMITED. - Roles and permissions reference: what
FORBIDDEN,KEY_SCOPEandTOKEN_SCOPEcheck. - Troubleshooting: fixes for rejected spans, missing traces and failing requests.
- Gateway errors: the error format of the gateway.
- Reconcile cost with your provider bill: billing sources, their refresh and the cost CSV.
- Daily review: the review policy and its manual runs.