Errors

All errors follow a standard format:

JSON
{
  "error": {
    "type": "not_found",
    "message": "Agent not found",
    "code": 404,
    "request_id": "req_abc123",
    "doc_url": "https://www.m8tes.ai/docs/api-errors#error-types"
  }
}

doc_url links to the docs page for that error type. Include request_id when you contact support.

FieldUse it for
error.codeHTTP status, such as 402
error.error_codeRecovery logic, such as TOKEN_BALANCE_DEPLETED
error.detailsRecovery URLs, limits, and other context
error.messageHuman-readable explanation

Branch on error_code, never message text. Older clients can read error.details.error_code; Python exposes it as the exception's .code or .error_code.

Error Types

The four you'll hit while building:

StatusTypeSDK ExceptionDescription
401authentication_errorAuthenticationErrorInvalid or missing API key
402billing_errorBillingErrorRun or cost limit reached, or balance depleted
422validation_errorValidationErrorInvalid request parameters
429rate_limit_errorRateLimitErrorToo many requests

Catch the exception you can recover from:

Python
from m8tes import M8tes, NotFoundError, AuthenticationError, BillingError, RateLimitError

client = M8tes()

try:
    agent = client.agents.get(999, user_id="customer_42")
except NotFoundError:
    print("Agent does not exist")
except AuthenticationError:
    print("Check your API key")
except BillingError as e:
    print(e.code, e.details)  # machine-readable recovery context
except RateLimitError:
    print("Still rate limited after SDK retries. Try again later.")

The SDK retries 429 and 500+ responses with exponential backoff for idempotent methods (GET, PUT, DELETE), and for the run-creating POSTs, which carry an idempotency key so a retry returns the original run instead of starting a second one. Any other POST or PATCH is never retried, to prevent duplicate side effects.

All error types and `error.details` conventions
StatusTypeSDK ExceptionDescription
400invalid_request_errorValidationErrorInvalid request
403permission_errorPermissionDeniedErrorInsufficient permissions
404not_foundNotFoundErrorResource not found
405method_not_allowedAPIErrorHTTP method not supported on this path
409conflict_errorConflictErrorResource conflict (duplicate, not active)
500+api_errorAPIErrorServer error (automatically retried for GET/PUT/DELETE)
  • Errors that carry a machine-readable app code (e.g. feature_disabled, from_template_conflict, RUN_LIMIT_REACHED) put it in error.error_code (mirrored at error.details.error_code for older clients), with any recovery context (missing, connect_urls, rejected_fields, …) as sibling keys under error.details. The Python SDK surfaces it as the exception's .code (and .error_code since 4.8.0).
  • 403 TEAMS_MODEL_LOCKED: a Teams account set a model other than deepseek-v4-1-flash on an agent, task, or run. Omit model (or use that one), connect your own provider, or ask about Enterprise for every model.
  • 400 MODEL_NOT_RUNNABLE: the chosen model has no connected provider or gateway route that can fund it for this write's scope. Prefer a runnable: true row from GET /api/v2/models for account-scoped pins (the list is account-scoped); an end-user-scoped write may still succeed when that tenant's prepaid/gateway lane can fund a row that is false on the list. Connect the matching provider, or leave Default.
  • Query params are strict: an unknown query parameter is rejected with 422 (error.details.error_code = "unknown_query_parameter", with a did-you-mean hint). Analytics params (utm_*, gclid, fbclid, _) are ignored. Unknown fields in request bodies are silently ignored.

Billing Error Codes

For 402, read error.error_code and error.details (BillingError.code and .details in Python):

codeMeaningKey details fields
RUN_LIMIT_REACHEDMonthly included runs exhaustedruns_used, runs_limit, period_end, overage_available (whether overage can actually be enabled for your account; false without an active subscription to bill it to), free_path_available (false when already on Hobby — model-plan connect is not recovery past the allotment)
STARTER_LANE_MODEL_REQUIREDWallet still on the $1 API test credit — only deepseek-v4-1-flash is allowed until you top up or connect a providerallowed_model, requested_model, topup_url, connect_model_url
TOKEN_BALANCE_DEPLETEDPrepaid token balance exhausted (API/developer billing). Top up, or connect a model subscription for personal developmentbalance_micros, balance_usd, topup_url, connect_model_url (legacy: connect_claude_url)
Python
from m8tes import BillingError

try:
    run = client.runs.create_and_wait(agent_id=mate.id, message="...", user_id="customer_42")
except BillingError as e:
    if e.code == "RUN_LIMIT_REACHED" and e.details.get("overage_available"):
        client.billing.set_overage(enabled=True, monthly_cap_cents=5000)  # keep running
    elif e.code == "TOKEN_BALANCE_DEPLETED":
        checkout_url = client.billing.topup(amount_cents=5000)  # add $50 of credit
        print("Top up to continue:", checkout_url)
    else:
        print(e.code, e.details)
All billing codes, plus the two 429 guards
codeMeaningKey details fields
OVERAGE_CAP_REACHEDUsage overage spend cap hitoverage_used_cents, overage_cap_cents, period_end, overage_settings_url (raise the cap or upgrade)
TRIAL_EXPIREDTime-boxed trial has endedtrial_ends_at, upgrade_url
COST_LIMIT_REACHEDFair-usage cost backstop hitruns_used, runs_limit, period_end
SUBSCRIPTION_BLOCKEDPayment issue (past_due / unpaid)subscription_status
FUNDING_REQUIREDNo usable model provider or paid plan. When free_path_available is true, connect a model provider to continue for free; otherwise recovery is paid-only.plan, free_path_available, connect_model_url, upgrade_url, demo_url, demo_grants_access
END_USER_RUN_LIMIT_REACHEDOne end-user hit your per-end-user run sub-cap (other end-users unaffected)end_user_id, runs_used, runs_limit, period_end
END_USER_COST_LIMIT_REACHEDOne end-user hit your per-end-user cost sub-capend_user_id, cost_used_cents, cost_limit_cents, period_end

Two more guards return 429 rate_limit_error (not 402):

  • END_USER_RATE_LIMITED: an end-user burst past your per_end_user_rate_per_minute setting. details carry end_user_id, limit_per_minute, retry_after, plus a Retry-After header. Configure all three sub-caps via client.settings.update(...); see Users.
  • SANDBOX_CONCURRENCY_LIMIT: too many runs in flight at once. details.scope says which ceiling: account (your own burst; details carry your concurrent_runs and limit, plus upgrade_url when a bigger plan runs more at once) or platform (overall capacity, no counts). Wait for in-flight runs to finish and retry after the Retry-After header. The cap depends on your plan (Concurrent runs); upgrade or contact support for more.

Run-Level Failures

The exceptions above cover problems reaching the API. A run can also fail upstream: an expired Claude credential, an exhausted quota, a model rate limit. The HTTP request succeeds (no exception), but the run ends with status "failed", the message in run.output, and a machine-readable class in run.error_code. Always check it before trusting output:

Python
run = client.runs.create_and_wait(agent_id=mate.id, message="...", user_id="customer_42")
if run.error_code:
    print(f"run failed upstream: {run.error_code}: {run.output}")
else:
    print(run.output)
Run `error_code` reference

Transient codes are retry-safe; credential codes need re-auth first.

codeMeaningRun statusRetry safe?
oauth_revokedThe connected model-plan credential was revoked or expiredfailedAfter reconnecting OAuth
subscription_quota_exhaustedThe connected Claude, OpenAI/Codex, xAI/Grok, or Gemini plan hit its usage quotafailedAfter the provider quota window resets
model_unavailableThe requested model is not available on the connected provider planfailedAfter switching to a matching model or connecting that provider
model_pin_removedThe pinned model is no longer in the catalogfailedNo. Pick a current model, or clear the pin
api_credit_balanceThe upstream provider key ran out of creditfailedAfter the balance is restored
rate_limitedUpstream model rate limit hit mid-run (auto-Continue on own-sub + scheduled). A run held before it started is paused, not failed: see a run that has not startedfailedYes
overloadedThe upstream provider was overloaded or down, or its connection dropped before the reply finished (5xx/overload/dropped stream; auto-Continue on own-sub + scheduled)failedYes
tool_limit_exceededThe model provider rejected the request because it contained more tools than the provider allowsfailedAfter reducing connected tools or changing the integration set
safety_refusalThe model provider's safeguards flagged the request and the model declined to answer (for Claude, a usage-policy refusal). This can happen with ordinary requestsfailedAfter rephrasing the request or switching to a different model. Never retried automatically
provider_client_outdatedThe model provider refused the request because the client the platform used to reach it is older than the provider accepts (HTTP 426). A platform-side problem, not the account'sfailedNo. The same client version gets the same answer; we update it (reported automatically). Never retried automatically
llm_gateway_billing_errorThe model service the platform pays for refused the request over a billing problem on the platform side (HTTP 402). A platform-side problem, not the account's; earlier work in the run may already be donefailedNot immediately. The same refusal repeats until we settle it (reported automatically); wait, then retry. Never retried automatically
sandbox_boot_timeoutThe sandbox never started; nothing executedfailedYes (auto-retried on scheduled runs)
sandbox_quota_exhaustedThe execution platform was at capacity, so no sandbox could be created; nothing executedfailedNot immediately. The same limit will refuse a retry; wait, then retry
incomplete_streamThe stream died before finishing; the agent may have partially executedfailedReview output first
snapshot_version_mismatchThe sandbox image speaks a different agent-runtime contract than the platform. Cold starts older than the one-pin compat window usually auto-heal onto a fresh image; a second mismatch, a rollback (newer bake than the platform), or a mid-session incompatible sandbox fails closedfailedOnly when auto_retryable is true (heal pending). Otherwise no — retrying the same image cannot help; we update the pin (reported automatically)
internal_timeoutAn internal service timed out while the run was starting or executingfailedYes
service_unreachableA service the run depends on was unreachablefailedYes
sandbox_unavailableThe sandbox could not be provisioned, or was lost mid-runfailedYes (auto-retried on scheduled runs when nothing had executed yet)
storage_errorA temporary storage error interrupted the runfailedYes
deploy_interruptedA platform deploy interrupted the active process; task runs (including scheduled runs) with a saved session resume in placefailed, then running if recovery claims itAutomatic for eligible task runs; otherwise retry
orphaned_on_restartThe run was interrupted before it finished — by a restart, or by a worker that was killed; found at startup, or within minutes by the ownership leasefailed, then running if recovery claims itAutomatic for eligible task runs; otherwise retry
stuck_running_no_activityThe run stopped producing activity and its process could not recover itfailedYes; review partial output first
subagent_stopped_at_completionDelegated work was still running when the parent turn endedfailedResumes automatically: silently in the run viewer, and server-side for scheduled and triggered runs; no action needed
error_max_turnsThe run used every conversation turn its max_turns budget allowed. A new message or Continue re-arms the budget on that run; a retry starts a child without the parent's max_turns. The Platform run viewer auto-Continues oncefailedContinue / reply (preferred); retry creates a child with a fresh/default budget. Raise max_turns if the work genuinely needs more in one request
error_max_budget_usdThe run reached the spend budget set for the requestfailedContinue / reply starts a fresh turn (budget is unchanged); reduce what the work asks for in one turn, or start a new run with a higher budget
error_max_structured_output_retriesThe run's final answer did not match the requested output_schema within the allowed attempts; earlier work in the run may already be donefailedAfter checking the schema: a new message, or a retry
context_compact_thrashThe SDK aborted after compacting three times in a row because the conversation refilled to the context limit. A new message or Continue starts a fresh turn; retrying the same turn hits the same wallfailedContinue / reply; do not retry the same turn
prompt_too_longThe conversation exceeded the model's context window, including when automatic compaction failed. Earlier work and messages remain on the original runfailedNot automatic. Continue resumes the same conversation and can hit the same limit. A task retry starts fresh; a chat retry replays the failed turn in the same conversation and can hit the limit again. Review partial work before retrying; a retry can repeat actions
internal_errorUnclassified platform error; reported automatically on our sidefailedYes, then contact support
unknownUnclassified upstream error; the message is in run.outputvariesReview output first

Interrupted task runs continue their saved session, preserving the run ID and conversation. A non-null next_retry_at means automatic recovery is queued; keep polling the run or rejoin its stream when its status returns to running. Recovery checks every minute starting three minutes after server startup, waits for any platform update to finish, and uses the shared three-attempt limit. A newer run of the task, cancellation, or unavailable funding can stop recovery; next_retry_at then clears and manual recovery is available. Guarded coding runs cannot resume their discarded sandbox session and require manual recovery. Scheduled failures without a saved session retain their existing fresh retry policy, which refuses to replay work after external writes.

Cancel pending recovery with POST /api/v2/runs/{run_id}/cancel or client.runs.cancel(run_id). A queued failed run becomes cancelled and its next_retry_at clears, preventing either recovery worker from starting it.

A run that has not started

When your connected model subscription has stated when its usage limit resets, a new run that has nowhere else to go is not started just to be refused again. Nowhere else means no second account of that provider with room, no other connected provider outside its cooldown, and no credits. The run is held:

FieldValue
statuspaused
error_coderate_limited
stop_reasonThe limit message, with the reset time
next_retry_atThe reset time the provider stated. The run starts then, within 30 seconds
can_continuetrue

It has no sandbox and does not count toward your concurrent runs. It is not a failure: no run.failed webhook fires, and polling sees paused, then running.

  • Start it now: reply to it. Continue starts it with its first message and sends nothing after it. Any other message starts it and is delivered as its next turn. A reply with files or per-reply overrides answers 409. A reply in its Slack thread starts it too.
  • No run slot free: Continue answers 409 and can be sent again. Any other reply is queued, and the run starts as soon as a slot frees up.
  • Stop it: POST /api/v2/runs/{run_id}/cancel. It becomes cancelled.
  • Reconnect the subscription: the run starts within 30 seconds.
  • A newer run of the same schedule: only the newest held run of a schedule starts. Older ones become cancelled.
  • Archive it: an archived run is not started. At its reset it becomes cancelled. Unarchive it before then and it starts.

A run is only held on a reset time the provider stated, for the same account, credential and model, and never past that time.

For oauth_revoked, subscription_quota_exhausted, model_unavailable, and an own-subscription rate_limited failure, manual recovery is the exception to model pinning: retrying a task or replying to a chat resolves the account's current connected/preferred provider instead of replaying the inaccessible concrete model. Connect or select another provider first to switch; a chat reply resends only that failed follow-up on the same run. Other failure retries keep the original model.

Block before executionRecorded result
Billing or account gateFailed run with the gate's code; nothing executed
Gate has no codeADMISSION_BLOCKED
Recurring schedule: FUNDING_REQUIRED, MODEL_CONNECTION_REQUIRED, or CLAUDE_CONNECTION_REQUIREDSkip without a failed run; try the next occurrence
Three consecutive skips aboveSchedule pauses (auto_disabled); one email sent; re-arms when the gate clears

Account gates include EMAIL_VERIFICATION_REQUIRED, ACCOUNT_SUSPENDED, ACCOUNT_DELETED, and API_WALLET_REQUIRED. Restart a blocked one-off run after resolving the gate.

Next: Limits · Going Live

Was this page helpful?