The Xora API uses consistent error responses across all endpoints. This guide covers the error format, common error codes, and best practices for handling failures.
Every error response follows this structure:
"message" : " Human-readable description of what went wrong " ,
Field Type Description codestring Machine-readable error code (always UPPER_SNAKE_CASE) messagestring Human-readable explanation retryableboolean Whether the request can be retried
When a job fails, the error also includes a stage field indicating where in the pipeline the failure occurred:
"code" : " TRANSCODE_ERROR " ,
"message" : " FFmpeg exited with code 1: Invalid data found when processing input " ,
Code HTTP Status Meaning UNAUTHORIZED401 Missing or invalid API key / session NOT_FOUND404 Job, preset, file, or resource not found VALIDATION_ERROR400 Request body failed schema validation
Code Meaning Retryable ENGINE_ERRORProcessing failure Usually yes ENGINE_INVOCATION_ERRORProcessing timed out or crashed Yes ENGINE_INVOKE_FAILEDCould not start the job Yes ENGINE_EMPTY_RESPONSENo result was returned Yes ENGINE_INVALID_RESPONSEUnexpected response format Yes CONFIG_ERRORServer misconfiguration No UNSUPPORTED_COMMANDFFmpeg args rejected by security validation No TRANSCODE_ERRORFFmpeg processing failed Sometimes
Scenario Example message Missing input URL "input.url is required for this recipe"Invalid output format `“Invalid enum value. Expected ‘mp4' Bad FFmpeg args "ffmpeg.args must contain at least 1 element"Concat with single file "concat recipe requires input_files with at least two entries"Mixed input modes "Use either input/output or input_files/output_files, not both"Invalid webhook URL "webhookUrl must use https://"
When retryable is true, you can retry the request. For job failures, use the retry endpoint:
curl -X POST https://api.xora.sh/v1/jobs/JOB_ID/retry \
-H " Authorization: Bearer YOUR_API_KEY "
const response = await fetch ( ' https://api.xora.sh/v1/jobs/JOB_ID/retry ' , {
' Authorization ' : ' Bearer YOUR_API_KEY '
const data = await response . json ();
response = requests. post (
" https://api.xora.sh/v1/jobs/JOB_ID/retry " ,
" Authorization " : " Bearer YOUR_API_KEY "
"jobId" : " 01JXYZ1234ABCDEF56789000 "
For transient API errors (5xx), use exponential backoff:
async function callXoraApi ( url , options , maxRetries = 3 ) {
for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
const response = await fetch ( url , options );
if ( response . ok ) return response . json ();
const body = await response . json ();
// Don't retry client errors (4xx) — they won't succeed
if ( response . status < 500 ) throw new Error ( body . error . message );
// Retry server errors with exponential backoff
if ( attempt < maxRetries - 1 ) {
const delay = Math . pow ( 2 , attempt ) * 1000 ; // 1s, 2s, 4s
await new Promise ( resolve => setTimeout ( resolve , delay ));
throw new Error ( ' Max retries exceeded ' );
def call_xora_api ( url , method= " GET " , max_retries= 3 , **kwargs ) :
for attempt in range ( max_retries ):
response = requests. request ( method , url , ** kwargs )
if response.status_code < 400 :
# Don't retry client errors (4xx) — they won't succeed
if response.status_code < 500 :
raise Exception ( body [ " error " ] [ " message " ] )
# Retry server errors with exponential backoff
if attempt < max_retries - 1 :
delay = ( 2 ** attempt) # 1s, 2s, 4s
raise Exception ( " Max retries exceeded " )
Status Code When 400 VALIDATION_ERRORInvalid request body 401 UNAUTHORIZEDBad or missing auth 404 NOT_FOUNDPreset not found (when using presetId) 500 ENGINE_ERRORServer failed to accept the job
Status Code When 401 UNAUTHORIZEDBad or missing auth 404 NOT_FOUNDJob doesn’t exist or belongs to another user 500 ENGINE_ERRORServer unreachable
Status Code When 401 UNAUTHORIZEDBad or missing auth 404 NOT_FOUNDJob not found 500 ENGINE_ERRORServer failed to cancel
Status Code When 401 UNAUTHORIZEDBad or missing auth 404 NOT_FOUNDJob or output not found
Tip
Always check retryable — only retry when the error says it’s safe to do so.
Use exponential backoff — don’t flood the API with rapid retries.
Log the full error — the code, message, and stage fields together give you the complete picture.
Handle 401 early — if you get UNAUTHORIZED, check your API key before retrying.
Set up webhooks — webhook payloads include error details, so you can handle failures without polling.
When a job fails, the stage field tells you where in the pipeline it broke:
Stage What was happening probingAnalyzing the input file planningDeciding how to process segmentingPreparing the input for processing transcodingRunning FFmpeg mergingAssembling the final output
This helps you diagnose whether the issue is with your input file, your FFmpeg command, or a transient server problem.