错误与重试
统一错误信封、retryable 重试规则与完整错误码表。
下表自动生成于 Beatra 的公共运行时错误分类。
Every public Beatra error response uses the standard error envelope. The error.code field is one of the stable public values below; the error.retryable boolean tells the client SDK whether a naive retry could succeed.
Branching rules for client SDKs
retryable: trueerrors can be retried with the exact same request after a brief backoff. If aRetry-Afterheader is present, honor it.retryable: falseerrors will produce the same outcome on retry; the client must either change the request, fix credentials, or surface the error to the user.error.param(when present) is a JSON Pointer (RFC 6901) to the offending field in the request body.error.type,error.message,error.details, anderror.request_idprovide the stable family, safe explanation, correction facts, and trace identifier.
Auth / Account
| code | HTTP | retryable | description |
|---|---|---|---|
unauthorized | 401 | no | Missing or malformed authentication credentials. Send Authorization: Bearer <API key>. |
invalid_api_key | 401 | no | API key is invalid, disabled, revoked, or unknown. Create or rotate keys in the console (Settings → API Keys) and retry with the new key. |
api_key_region_mismatch | 400 | no | API key region prefix does not match this endpoint. Call the API host matching the key's region, or use a key issued for this region. |
tenant_disabled | 403 | no | Tenant has been suspended or disabled. Contact support; retrying cannot succeed. |
forbidden | 403 | no | Caller does not have permission for this operation. Use a credential with the required permission; retrying unchanged cannot succeed. |
email_send_failed | 502 | yes | Login-code email could not be delivered (transient send failure). Request a new code after a brief backoff. |
Billing
| code | HTTP | retryable | description |
|---|---|---|---|
insufficient_balance | 402 | no | Insufficient credits: this request was not started and nothing was charged. Add credits at https://console.beatra.ai/topup — they take effect immediately; then retry this same request. No reinstall or re-authorization is needed. |
billing_failed | 500 | yes | Charge or refund attempt failed after retries. Retry with the same Idempotency-Key / client_request_id; the identity guarantees no double charge. |
payment_provider_unavailable | 503 | no | Payment checkout provider is not configured for this payment method. Choose another payment method in the console. |
Request validation
| code | HTTP | retryable | description |
|---|---|---|---|
invalid_request | 422 | no | Request body fails structural validation. param points to the first offending field; details.errors lists every failure with its loc and reason. Fix the named fields and resend. |
skill_package_format_invalid | 422 | no | Skill package identity is malformed. Use a lowercase hyphenated slug of at most 64 characters and a valid SemVer package version. |
unsupported_capability | 400 | no | Capability does not exist or is disabled. GET /v1/catalog/capabilities lists the currently available capabilities. |
unsupported_model | 400 | no | Model does not exist or is not selectable for this capability. Discover selectable models with beatra.models.list or GET /v1/catalog/models, or use model=auto. |
unsupported_option | 422 | no | The selected concrete model does not support a requested value or control. param identifies the field and details.candidate_failures[] carries a specific failure code such as unsupported_resolution, unsupported_duration, or unsupported_aspect_ratio, plus the received and supported values. Change the named field or choose a different model; with model=auto this is returned only when no candidate can satisfy the request. |
no_eligible_model | 422 | no | No live model can honor the complete model=auto request. details.candidate_failures[] lists each provider-neutral rejection; change only the incompatible fields or pick a listed concrete model. |
voice_model_incompatible | 422 | no | The requested voice does not resolve on the requested model. Use model=auto, a compatible model, or another voice from the voices list. |
unsupported_input_modality | 422 | no | The selected model does not support an input modality in the request (such as image, audio, or file input). param points to the first offending field; remove it or choose a model that supports it. |
context_length_exceeded | 422 | no | The request exceeds the selected model's context window. Shorten the input or choose a model with a larger context window. |
safety_rejected | 400 | no | Input was rejected by safety policy; details.policy_category names the category when available. Rephrase the input; retrying unchanged cannot succeed. |
content_filtered | 502 | no | A safety filter blocked the output mid-generation. Rephrase the prompt or switch models; an identical retry is usually blocked again. |
not_found | 404 | no | Requested HTTP resource or endpoint was not found. Check the path and identifier; retrying unchanged cannot succeed. |
streaming_not_yet_implemented | 501 | no | Legacy compatibility code for older deployments where stream=true was recognized but not enabled. |
Callbacks
| code | HTTP | retryable | description |
|---|---|---|---|
callback_url_required | 422 | no | A callback URL is required when a callback signing key is selected. Add callback_url or remove callback_signing_key_id. |
callback_signing_key_not_found | 404 | no | Callback signing key does not exist or is not visible to this account. List signing keys in the console and pass a current key id. |
callback_signing_key_inactive | 422 | no | Callback signing key is retired and cannot be selected for a new task. Select an active key, or omit the key id for unsigned delivery. |
Media inputs
| code | HTTP | retryable | description |
|---|---|---|---|
invalid_media | 422 | no | Media input is malformed or cannot be decoded. Re-export or re-encode the file and submit it again; param names the offending field. |
unsupported_media_type | 422 | no | Media input type is not supported for this capability. Convert to a supported format (see the capability page or model constraints) and resend. |
media_too_large | 413 | no | Media input exceeds the allowed size. Compress or trim the input, or upload it first and pass an artifact reference. |
media_fetch_failed | 422 | yes | Beatra or the provider could not fetch the referenced media URL. Ensure it is publicly reachable over HTTPS, or upload the file and pass an artifact reference instead. |
Tasks and resources
| code | HTTP | retryable | description |
|---|---|---|---|
task_not_found | 404 | no | task_id does not exist or does not belong to this account. Use the task_id returned at creation; list recent tasks with GET /v1/tasks or beatra.tasks.list. |
conflict | 409 | no | The operation conflicts with the resource's current state — for task cancellation this means execution continues. Follow details.action (usually: keep polling the same task); do not resubmit the work. |
artifact_not_found | 404 | no | artifact_id does not exist or does not belong to this account. Upload the media again and use the newly returned artifact id. |
voice_not_found | 404 | no | voice_id does not exist or does not belong to this account. List available voices with GET /v1/voices or beatra.voices.list. |
Idempotency and response mode
| code | HTTP | retryable | description |
|---|---|---|---|
idempotency_conflict | 409 | no | Same Idempotency-Key / client_request_id was used with a different request body. Use a new identity for a changed request. |
request_in_progress | 409 | yes | Same idempotency identity matches an in-flight request. Retry later with the same identity; the original response will be returned. |
Capacity and availability
| code | HTTP | retryable | description |
|---|---|---|---|
rate_limited | 429 | yes | Caller-side rate limit exceeded. Retry after the Retry-After interval; X-RateLimit-* headers report the active budget. |
model_unavailable | 503 | yes | The requested model is temporarily unavailable. Retry with backoff; if it persists, switch models or use model=auto. |
model_request_failed | 502 | yes | The model request failed after retryable service errors. Retry with backoff; if it persists, adjust inputs or choose another model. |
model_timeout | 504 | yes | The model request timed out. Retry with backoff. |
model_rate_limited | 429 | yes | Model capacity was rate-limited. Retry after a backoff. |
model_output_invalid | 502 | no | A generated artifact did not satisfy the admitted resolution, duration, aspect-ratio, metadata, or MIME contract. Inspect param, details.expected, and details.actual; the task is failed and the artifact is not published. |
internal_error | 500 | yes | Unclassified server-side error. Retry with the same idempotency identity; if it persists, report the request_id. |
Asynchronous task failures
A generation can be admitted successfully and later end with status: failed. REST task reads, MCP beatra.tasks.get, and callbacks return the same Task.error object with code, type, message, retryable, optional param, optional details, and request_id. A failed task read is the task's final state, not an MCP protocol error.
For model_output_invalid, use Task.error.details.expected and Task.error.details.actual to report the output-contract mismatch. Do not publish the rejected artifact or retry a non-retryable error unchanged.
MCP tool errors
A failed MCP tool call returns a tool result with isError: true and structuredContent.error carrying the same vocabulary as the REST envelope — code, type, message, retryable, optional param (JSON Pointer into the tool arguments), optional details — plus the JSON-RPC jsonrpc_code. Branch on code and retryable exactly as with REST.
One MCP-only code exists: insufficient_scope (the OAuth grant lacks a required scope; missing_scopes names them — re-run the Beatra device authorization; retrying without re-authorizing cannot succeed). Unexpected server failures surface as internal_error tool results, never as bare HTTP errors.
Generated from 42 public runtime error codes. 25 internal, admin, or provider-ingress codes are intentionally omitted from public docs.
如何处理错误
每个错误体均为:
retryable: true-> 短暂退避后重发同样请求(携带相同的Idempotency-Key)。如响应带有Retry-After请遵守;429响应 附带X-RateLimit-*头。retryable: false-> 必须先调整请求再重试。在不可重试错误上死循环是 bug。