Skip to content
HomeConsoleGet started

Every runstate SDK error class and when it is raised, what the SDKs retry for you, and the error envelope and common codes of the HTTP API.

Every SDK error is a RunstateError with four fields:

Field (TS / Python) Meaning
code / code A stable error code. Branch on the class or the code, never on the message.
status / status The HTTP status, or 0 for errors raised in your process.
requestId / request_id Include it when you report a problem.
retryAfter / retry_after Seconds to wait before trying again, or null / None.

The classes are the same in TypeScript and Python:

Class When What to do
ConfigError Missing key or space, bad durations or amounts, arguments that can’t be hashed. Fix the configuration or pass key.
AuthError Bad key, missing permission, suspended space, or a plan limit reached. Check the key and its permissions, or your plan.
StoppedError The run was stopped, finished or passed its deadline. Stop taking work for this run.
TimeoutError A wait (limit, slots, take, together, a job result) ran out. Try again later, or use a longer timeout. The work on the server is unaffected.
OnceFailedError A once function failed attempts times. Has attempts and message. Fix the cause. A fresh run tries again.
TaskFailedError A queue job failed after its attempts, or a gather can’t reach need. Has jobId / job_id and reason. Read reason.
BudgetExceededError A reservation doesn’t fit in the budget. Don’t start the work. The function was not called.
ResultTooLargeError An input or result is over 64 KB. Store the data elsewhere and pass a reference.
ConflictError Sizes in code don’t match the existing limit, slots or budget, or a queue’s attempts differ between processes. Make the code match, or change the resource in the console.
LeaseLostError The process lost its hold on once or slots work because someone else took it over. Queue jobs don’t raise it: the worker’s result is dropped quietly. Stop. Don’t record a result for this work.
UnavailableError runstate couldn’t be reached after retries. Retry later.

In Python, runstate.TimeoutError is also a subclass of the built-in TimeoutError.

Situation Retried automatically?
Network error or request timeout Yes: up to retries times (default 3), reusing the same Idempotency-Key. Then UnavailableError.
Slots all in use Yes. The call waits for a free slot until its timeout (default 60 s), then TimeoutError.
Limit window used up Yes. The call waits in a fair queue on the server until its timeout (default 60 s), then TimeoutError.
Rate limited or plan concurrency limit reached Yes, honouring Retry-After. Only a timeout running out surfaces as an error.
once function throws Yes. The next waiting caller runs it again, up to attempts (default 3). Then OnceFailedError.
Queue handler throws Yes. The job is retried with backoff up to attempts (default 5). Then TaskFailedError.
Lost lease Never. The SDK aborts context().signal. For once and slots it raises LeaseLostError. For a queue job it drops the worker’s result quietly, and the caller gets the result of the worker that finished the job.
Missing permission, bad key, budget too small No. Raised straight away.
import runstate, { BudgetExceededError, OnceFailedError, RunstateError, StoppedError, TimeoutError } from 'runstate-sdk';
const ask = runstate.budget('model-usd', callModel, { reserve: 0.4, cost: (r) => r.usd });
async function summarize(text: string) {
try {
return await ask(text);
} catch (err) {
if (err instanceof BudgetExceededError) return null; // budget used up: the call never started
if (err instanceof StoppedError) return null; // the run is over
if (err instanceof TimeoutError || err instanceof OnceFailedError) throw err;
if (err instanceof RunstateError) {
console.error(`runstate ${err.code} (${err.status}), request ${err.requestId}`);
}
throw err;
}
}

If you call the HTTP API directly, every error uses one envelope:

{
"error": {
"code": "INSUFFICIENT_BUDGET",
"message": "reservation exceeds available budget",
"requestId": "…"
}
}

Codes and their HTTP statuses are a versioned contract. Branch on code, never on message. Common codes:

Code HTTP Typical cause
VALIDATION_FAILED 400 Malformed body or parameters, missing Idempotency-Key, payload over the size limit.
UNAUTHENTICATED 401 Missing or invalid API key.
FORBIDDEN 403 The key lacks the permission for this call, or isn’t allowed for this space.
ENTITLEMENT_EXCEEDED 403 A plan ceiling was hit, such as active runs or active jobs.
SPACE_SUSPENDED 403 The space was suspended by an owner. New work is refused; work already held can still be handed back.
NOT_FOUND 404 Unknown id or name, or an id from another organization.
CONFLICT 409 State conflict, or a resource declared with different sizes.
IDEMPOTENCY_CONFLICT 409 An Idempotency-Key or job key reused with a different request.
DEADLINE_EXCEEDED 409 The run’s deadline has passed.
CAPACITY_UNAVAILABLE 409 No free slots.
BACKLOG_FULL 409 The space’s queued-work limit is reached. See Limits.
INSUFFICIENT_BUDGET 409 The budget can’t cover the reservation.
RATE_LIMITED 429 The space’s requestsPerMinute limit was exceeded.
SERVICE_RATE_LIMITED 429 Your organization used its plan’s requests for this minute.
CONCURRENCY_LIMITED 429 Your organization is at its plan’s “agents working at once” limit.
OPERATIONS_EXHAUSTED 429 The free plan’s monthly operations are used up.
UNAVAILABLE 503 The service or the network failed.

Every code and the endpoints that return it are in the HTTP API reference.

Send an Idempotency-Key on every mutation. Reusing a key with the same body within 24 hours replays the stored response; with a different body it returns IDEMPOTENCY_CONFLICT. The SDKs do this for you.