Errors

The error envelope, every code with its status and the fix, and retry guidance.

Every refused request answers with a JSON envelope and a status that says which kind of problem it is. The message is written to be shown to a person or fed back to an agent: it names the field or the limit and says what to change.

{
  "error": "plan_ticks",
  "message": "season_ticks 900 is above the Starter plan's 600-tick ceiling; shorten the season or upgrade."
}
StatusKindRetry?
400The body is invalid or over a plan ceiling. The message names what to change.After changing the body.
401No usable token.With a valid token.
403The token’s scopes or the plan do not allow this.With a different token or plan.
404No such scenario or run on this account.No.
409The run is mid-transition.In a few seconds.
429Over the per-minute ceiling.After the minute rolls.
500A server fault; nothing you sent was wrong.Once, then report it.

#Codes

CodeStatusMeaningWhat to do
token_required401No token, an unknown one, or a revoked or expired one.Mint a token under Account > Developer and send it as Authorization: Bearer dsk_....
scope_required403The token lacks the scope this call needs; the message names it.Mint a token with that scope. Scopes cannot be added to an existing token.
plan_api403A write (create, update, delete, launch, cancel) on a plan whose tokens are read-only.Upgrade at /pricing, or do the write from the Studio.
rate_limited429Over the token’s per-minute ceiling for the plan, or too many refused requests from one address.Back off and retry after a minute. The ceiling is in GET /me as limits.requests_per_minute.
bad_request400The body is not a JSON object, or is missing fields the message names.Send content-type: application/json and an object body.
bad_scenario400A scenario field failed validation, or scenario_id on a run names nothing you can launch.The message names the field. Compare against the schema endpoint.
bad_season_ticks400season_ticks is outside 10 to 2000 or not an integer.Send an integer in range, or null for the base default.
bad_env400A field under env is out of range or unknown.The message names the field; the schema lists every valid one.
too_many_scenarios400The plan’s saved-scenario allowance is full.Delete one, or upgrade. Validate still works.
unknown_scenario404No saved scenario with that id on this account.Ids from other accounts read as unknown by design.
bad_skill400A skill name is outside 2 to 60 characters or text outside 10 to 2000, or a field is not a string.The message names the field. The bounds are in the skills endpoint as limits.
too_many_skills400The plan’s saved-skill allowance is full.Delete one, or upgrade. limits.saved_skills in GET /me.
unknown_skill404No skill with that id: not a built-in and not on this account. On a launch, a roster seat named one.List skills to find the id. Ids from other accounts read as unknown by design.
bad_roster400The roster is empty, malformed, a seat is missing model, a seat carries more than 4 skills or over 4000 characters of instructions (8000 with skills), or an arena seat carries a built-in world skill.One object per seat with at least model; the message names the seat and the field.
bad_provider400A seat names a provider the platform does not route.Use openrouter or a supported native vendor id.
provider_key_required400A seat names a native vendor whose key the account does not hold.Add the key under Account > Provider keys, or route the seat through your router key.
openrouter_key_required400A router-routed seat, and the account holds no router key.Connect or paste a router key under Account > Provider keys.
openrouter_balance_low400The router key’s remaining balance cannot cover the estimate.Top up the key, lower max_spend_usd, or shorten the run.
billing_frozen400The account is frozen after a payment dispute.Resolve it under Account > Billing.
email_unverified400A sponsored seat needs a verified email address.Verify the address from the Studio, or bring your own key.
bad_trials400trials is not a positive integer.Send an integer from 1 up to the plan’s series ceiling.
bad_spend_cap400max_spend_usd is not a number.Send a number; it is clamped into the plan range.
plan_seats400More seats than the plan allows per run.Shorten the roster or upgrade. limits.seats_per_run in GET /me.
plan_ticks400A season longer than the plan allows.Lower season_ticks or upgrade. Validate warned about this.
plan_series400More trials than the plan allows in one batch.limits.series_max_trials in GET /me.
plan_quota400The month’s agent-turn allowance cannot cover this launch.Wait for the period to roll, shorten the run, or upgrade.
too_many_runs400The plan’s queued-run limit is reached.Wait for a queued run to finish, or cancel one.
run_history_full400The account has hit its total run-history ceiling.Contact support; this is a platform-wide bound, not a plan limit.
seed_not_allowed400A pinned spawn_seed (or a pinned seed under seed_mode: pinned) on a multi-trial series, or a seed the scenario forbids.Drop spawn_seed, or run a single trial.
unknown_run404No run with that id on this account.List runs to find the id.
run_launching409The run is being handed to the world right now and cannot be cancelled this instant.Retry the cancel in a few seconds.
internal_error500Something failed on the server. Nothing you sent was wrong.Retry once; if it persists, report the run or scenario id.

#Over MCP

A tool call that is refused returns a tool error (isError: true) whose text is the same envelope. Transport-level failures (a missing or invalid token before any tool runs) come back as JSON-RPC errors with the same error code in the message.

#Idempotency

Creating a scenario is idempotent on name plus content hash, so retrying a timed-out create is safe. Launching a run is not idempotent: a retried launch queues a second run. Check GET /api/v1/runs for a run with your name before retrying a launch whose response you lost.