DocsReference
Errors
The shape of every error answer, the fifteen error codes with what to do about each, request ids, and how a failed job reports why.
Every error answers with RFC 9457 problem details, Content-Type: application/problem+json. The
code is stable and documented on this page, and so is its reason when it has one: programs
branch on them. detail is for people and may change.
{
"type": "https://videxo.io/docs/errors#invalid_request",
"title": "The request is not valid. `errors` lists each field that is wrong.",
"status": 400,
"detail": "outputs[1].fade.out: fade.out needs the output's length: set trim.duration or trim.end (or an end on every concat part).",
"code": "invalid_request",
"reason": "invalid_fields",
"retryable": false,
"request_id": "req_7h2m4x9q0c3v8k1n5r6t2b4z",
"errors": [
{
"pointer": "#/outputs/1/fade/out",
"detail": "fade.out needs the output's length: set trim.duration or trim.end (or an end on every concat part)."
}
]
}Copy failed. The text is selected; copy it with your keyboard.
| Member | Always | Meaning |
|---|---|---|
type | yes | https://videxo.io/docs/errors#<code>: the code's section below. |
title | yes | The code's fixed sentence. |
status | yes | The HTTP status, repeated. |
detail | yes | What happened this time; it names the first wrong field when there is one. |
code | yes | The stable error code. |
reason | when the code has reasons | The documented finer case, listed under each code. |
retryable | yes | Whether sending the same request again later can succeed without a change. |
request_id | yes | The id of this request, also in the Request-Id header. |
errors | with invalid_request | One entry per wrong field: a detail and where the field is, one of pointer (a JSON Pointer into the body, #/outputs/0/resize/width; # is the whole body), parameter (a query or path parameter) or header. |
problems | with command_invalid | Why the command is not accepted. Their codes are listed in Command rules. |
An unknown body field is refused, one errors entry each, so a typo never silently does nothing.
The same body is the content of an MCP tool error.
Request ids#
Every answer carries a Request-Id header (req_ and 24 letters and digits), successes included,
and an error repeats it as request_id. Quote it when you contact support. The browser upload URL
exposes the header to scripts too.
Failed jobs#
A failed job is not an error answer: GET /v1/jobs/{id} answers 200 with state: "failed" and an
error object, and the webhook and MCP tools carry the same job:
| Member | Meaning |
|---|---|
code | One of the codes below that a job can carry. |
reason | The documented finer case, or null. |
message | What happened, for people; it may change. |
retryable | Whether creating the same job again can succeed without a change. |
log_tail | With processing_failed and ffmpeg_error: ffmpeg's last lines about your media, at most 20 lines and 4 KB, with URLs, keys and our own file paths and options removed (inputs appear as {{in_...}}); otherwise null. |
A cancelled job has error: null. We retry what can pass on its own (an input server that
answered an error for a moment, a lost connection, a restart on our side) before a job fails;
a job that fails with internal_error or service_unavailable uses no credits.
Codes#
Answers with 401 carry WWW-Authenticate: Bearer; answers with 429 and 503 carry
Retry-After in seconds.
authentication_failed#
The API key, client token or access token is missing, malformed, unknown, revoked or expired.
A request answer; HTTP 401; trying again without a change fails the same way.
What to do: Send a valid key from the dashboard as Authorization: Bearer <key>. In an app, send a client token from your backend; ask your backend for a fresh one when it has expired. An MCP client that signs in follows the challenge and signs in again.
| Reason | Meaning |
|---|---|
missing_api_key | No API key was sent. |
invalid_api_key | The key is malformed, unknown or deleted. |
api_key_revoked | The key was revoked in the dashboard. Create a new one. |
api_key_expired | The key has expired. Create a new one. |
missing_client_token | No client token was sent to a client route. |
invalid_client_token | The client token is malformed, unknown or was changed. |
client_token_expired | The client token has expired. Ask your backend for a fresh one. |
client_token_superseded | A newer token of the same chain is in use. Use that one. |
client_token_revoked | The client token was revoked, with its chain or the key that created it. |
missing_credentials | The MCP server got no credentials. Sign in, send an API key, or use the public address for the tools that need neither. |
invalid_token | The access token is malformed or unknown, or was not issued for this server. A revoked token usually reads this way. Sign in again. |
token_expired | The access token has expired. Refresh it. |
token_revoked | The app's access has ended: it was revoked, the member left the organization, or the organization was deleted. Sign in again. |
permission_denied#
The request is not allowed.
A request answer; HTTP 403; trying again without a change fails the same way.
What to do: For a disabled account, contact support. For a signed link, ask for a fresh one. For a role, ask an owner or admin of the organization. For a scope, sign in again and allow it.
| Reason | Meaning |
|---|---|
account_disabled | The account is disabled and cannot create jobs. |
link_invalid | The signed link is not valid: it was changed or belongs to another file. |
origin_not_allowed | This browser origin may not make this call. |
role_insufficient | Your role in the organization does not allow this. Ask an owner or admin. |
token_scope | A client token cannot do this: it works only on the client routes its grant allows. |
client_token_required | This route takes a client token, never an API key. Keep API keys on your server. |
insufficient_scope | The access token does not cover this call. The WWW-Authenticate header names the scopes it needs: sign in again with them. |
invalid_request#
The request is not valid. errors lists each field that is wrong.
A request answer; HTTP 400, 415, 416; trying again without a change fails the same way.
What to do: Fix the fields that errors names.
| Reason | Meaning |
|---|---|
invalid_fields | A header, parameter or body field is missing or wrong. |
not_json | The body is not JSON, or its content type is not application/json. (HTTP 415) |
range_not_satisfiable | The requested byte range does not fit the file. (HTTP 416) |
size_mismatch | The uploaded body is not size_bytes long. Create a new upload with the exact length. |
command_invalid#
The command contains unsupported or invalid arguments. problems lists each one.
A request answer; HTTP 400; trying again without a change fails the same way.
What to do: Change the command; the command rules list what is supported.
not_found#
There is no such resource in this account, or no such route.
A request answer; HTTP 404; trying again without a change fails the same way.
What to do: Check the id, and that the API key belongs to the account that owns it.
| Reason | Meaning |
|---|---|
job | No job with this id in this account. |
file | The file does not exist or has been deleted. |
output | The job has no output with this name. |
upload | No completed, retained upload with this id in this account. |
webhook_endpoint | No webhook endpoint with this id in this account. |
webhook_delivery | No webhook delivery with this id in this account. |
route | There is no such route. |
subscription | The account has no subscription to change. |
ticket | No support ticket with this id in this account. |
client_token | No client token with this id in this account. |
conflict#
The resource's state does not allow this now.
A request answer; HTTP 409; trying again without a change fails the same way.
What to do: Read reason: wait for the job or the delivery, create a new upload, change the endpoint with PATCH, or use a new Idempotency-Key for a new request.
| Reason | Meaning |
|---|---|
job_active | The job is still queued or running. Its files can be deleted once it ends. |
upload_used | This upload URL has already been used. Create a new upload. |
webhook_endpoint_exists | The account already has its webhook endpoint. Change it with PATCH or delete it first. |
webhook_delivery_pending | The delivery is still being attempted. Send it again once it is delivered or failed. |
idempotency_key_reused | This Idempotency-Key was used in the last 24 hours with a different request. |
checkout_pending | A checkout for another plan is open. Finish it, or start again in 30 minutes. |
subscription_exists | The account already has a subscription. Change its plan instead. |
same_plan | The account is already on this plan. |
ticket_closed | The ticket is closed. Reopen it to write again. |
resource_expired#
The file, the upload or the signed link has expired.
A request answer; HTTP 410; trying again without a change fails the same way.
What to do: Ask for a fresh link while the file is retained, or upload the file again.
| Reason | Meaning |
|---|---|
link_expired | The signed link has expired. Ask for a fresh one. |
file_expired | The file passed the plan's retention and was deleted. |
upload_expired | The upload expired 24 hours after it was created. |
limit_exceeded#
A plan or request limit was passed.
A request answer or a failed job; HTTP 413; trying again without a change fails the same way.
What to do: Use smaller inputs or a shorter job, or move to a plan with larger limits (limits).
| Reason | Meaning |
|---|---|
request_body_size | The request body is larger than the API accepts. |
upload_size | The upload is larger than the plan's input limit or the 100 MB direct upload limit. |
input_size | The job's inputs passed the plan's total input size. |
output_size | The job's outputs passed the plan's total output size. |
job_duration | The job ran past its time limit: the plan's longest job, or less when the period's credits left less room. |
member_limit | The organization has as many members as its plan allows. |
organization_limit | You own as many organizations as allowed. |
token_uploads | The client token's chain has created as many uploads as its grant allows. |
subtitles_size | A subtitles file is larger than a job accepts. |
usage_cap_reached#
The plan's overage cap for this period is reached.
A request answer or a failed job; HTTP 402; trying again without a change fails the same way.
What to do: Wait for the next period, or upgrade the plan.
out_of_credits#
The prepaid credit balance is at zero.
A request answer or a failed job; HTTP 402; trying again without a change fails the same way.
What to do: Credits cannot be bought yet: payments are not connected. Write to us if you need more.
rate_limited#
Too many requests. Try again after the number of seconds in the Retry-After header.
A request answer; HTTP 429; safe to try again.
What to do: Wait for Retry-After, then send the request again.
service_unavailable#
We could not complete this in time, after retrying on our side. Quote the request id to support if it repeats.
A request answer or a failed job; HTTP 503; safe to try again.
What to do: Send the request again after Retry-After. After cancel_unconfirmed, read the job before cancelling again. A job that failed with queue_timeout can be created again.
| Reason | Meaning |
|---|---|
temporary | Part of the service did not answer in time. Quote the request id to support if it repeats. |
cancel_unconfirmed | Cancellation could not be confirmed. Read the job before trying again. |
queue_timeout | The job waited 24 hours without a free slot, for the account or for the service. |
internal_error#
Something failed on our side. Quote the request id to support if it repeats.
A request answer or a failed job; HTTP 500; safe to try again.
What to do: Send the request again with the same Idempotency-Key; it answers the job if one was made. Create a failed job again with a new key. If it repeats, contact support with the request id or the job id. A job that failed this way uses no credits.
input_unreachable#
An input could not be read.
A failed job; no HTTP status: GET /v1/jobs/{id} answers 200 with the failed job; trying again without a change fails the same way.
What to do: Make the URL publicly reachable over http or https on the default port, or upload the file again.
| Reason | Meaning |
|---|---|
url_refused | The URL could not be reached, or is not a public http or https address on the default port. |
http_status | The server of the URL answered with an error. |
network | The URL could not be reached, after several tries. |
redirects | The URL redirected more than 5 times. |
too_slow | Fetching the inputs took longer than 20 minutes. |
upload_expired | The upload expired before the job could read it. |
processing_failed#
The media could not be processed with these settings.
A failed job; no HTTP status: GET /v1/jobs/{id} answers 200 with the failed job; trying again without a change fails the same way.
What to do: Fix the command, the settings or the media, then create the job again.
| Reason | Meaning |
|---|---|
ffmpeg_error | ffmpeg stopped with an error. |
output_empty | An output came out empty. |
time_past_end | A requested time or trim is past the end of the input. |
subtitles_invalid | The subtitles could not be read: check their format and encoding. |
media_unreadable | An input could not be read as audio, video or an image. |
Codes before November 2026#
The list was shorter and plainer from 2026-11-05 (changelog). Each earlier code answers as:
| Before | Now |
|---|---|
missing_api_key | authentication_failed, reason missing_api_key |
invalid_api_key | authentication_failed, reason invalid_api_key |
api_key_revoked | authentication_failed, reason api_key_revoked |
api_key_expired | authentication_failed, reason api_key_expired |
account_disabled | permission_denied, reason account_disabled |
file_signature_invalid | permission_denied, reason link_invalid |
idempotency_conflict | conflict, reason idempotency_key_reused |
job_active | conflict, reason job_active |
upload_used | conflict, reason upload_used |
webhook_endpoint_exists | conflict, reason webhook_endpoint_exists |
webhook_delivery_pending | conflict, reason webhook_delivery_pending |
job_not_found | not_found, reason job |
file_not_found | not_found, reason file |
upload_not_found | not_found, reason upload |
webhook_endpoint_not_found | not_found, reason webhook_endpoint |
webhook_delivery_not_found | not_found, reason webhook_delivery |
file_expired | resource_expired |
upload_too_large | limit_exceeded, reason upload_size |
upload_size_mismatch | invalid_request, reason size_mismatch |
range_not_satisfiable | invalid_request, reason range_not_satisfiable |
runner_unavailable | service_unavailable, reason cancel_unconfirmed |
input_too_large | limit_exceeded, reason input_size |
output_too_large | limit_exceeded, reason output_size |
time_limit | limit_exceeded, reason job_duration |
ffmpeg_failed | processing_failed, reason ffmpeg_error |
queue_timeout | service_unavailable, reason queue_timeout |