Errors¶
Every error from /api and /v1 has the shape OpenAI clients expect, with a stable code and, when a field is to blame, a param:
{"error": {"message": "...", "type": "invalid_request_error", "param": "version", "code": "entry_exists"}}
Some errors carry extra context at the top level: current on a 409 holds the entry as it is now, and nearest on a replay miss names the closest recorded prompt.
What your app sees¶
| Status | code |
When |
|---|---|---|
| 424 | semula_no_match |
Replay mode, and nothing recorded matches. nearest says what came closest. |
| 504 | semula_record_timeout |
Record mode, and nobody answered within SEMULA_RECORD_TIMEOUT (300 s by default). A late answer is still saved. |
| 429 | semula_upstream_budget |
The project forwards misses upstream and has used its upstream_max_calls. Raise the cap or reset the count (semula project upstream <id> --reset); recorded answers still replay. |
| 429 | semula_miss_ceiling |
The project has taken its max_misses for this run, so this call was not parked. Fix what keeps missing, then set max_misses again to start a new run. |
| 424 | semula_upstream_dry_run |
The project is in a dry run: this miss would have been forwarded and nothing was called. |
| 502, 504 | semula_upstream_error, semula_upstream_timeout |
The upstream provider refused, failed or did not answer. The message has its status; the key is never in it. The failed call is not counted against the cap. |
| 401 | invalid_api_key |
SEMULA_API_KEY is set and the request did not send it. |
| 404 | model_not_found |
GET /v1/models/<id> for a model this server does not list. Chat calls ignore the model name. |
What an agent or the CLI sees¶
The CLI exits 4 for 400, 409 and 422 and prints error: <code>: <message> (at <param>).
| Status | code |
Meaning and what to do |
|---|---|---|
| 422 | invalid_answer |
The answer is malformed. param says where. The call stays held: fix that field and send it again. |
| 409 | version_conflict |
Someone changed the entry since you read it. current holds the new one. Re-read and re-apply; never retry the stale write. |
| 409 | entry_pinned |
A human pinned this entry. Leave it alone and tell the user. Pass force only if told to. |
| 409 | entry_exists |
entry add or upsert_entry found an entry for that request. Pass its version to replace it. |
| 409 | already_answered |
The parked request was answered first by someone else. Move on. |
| 404 | pending_not_found |
No such parked request. It was answered or deleted. |
| 404 | entry_not_found, project_not_found, candidate_not_found |
The id does not exist. |
| 409 | candidate_decided |
The review candidate was already approved or rejected. |
| 409 | project_exists, default_project |
A project with that id exists, or you tried to delete default. |
| 409 | entry_in_other_project, alias_in_other_project |
An id belongs to a different project. |
| 400 | version_required |
An edit needs the entry's current version. |
| 400 | invalid_json, invalid_request, nothing_to_update |
The request body is not what the route expects. |
| 422 | invalid_mode, invalid_selection, invalid_pinned, invalid_settings, invalid_project_id, invalid_param, invalid_bundle |
A value is out of range or malformed. param names it. |
| 422 | answerer_not_configured |
answerer was turned on but the server has no SEMULA_ANSWERER_COMMAND or SEMULA_ANSWERER_URL. |
| 422 | upstream_not_configured |
record_through was turned on but the server has no SEMULA_UPSTREAM_KEY. |
| 503 | mcp_unavailable |
The MCP endpoint could not start. |
| 500 | internal_error |
A bug. Please report it with the log line. |