Understanding job status values and proper error handling is essential for building robust applications with the Queue API.
Job Status Values
Every job progresses through a series of states:
type JobStatus = "pending" | "processing" | "completed" | "failed" ;
Status Lifecycle
pending
Job is queued and waiting to start. This is the initial state after submission.
processing
Job is actively being processed by the backend. This is where the actual video generation happens.
completed
Job finished successfully. The result video is ready to download.
failed
Job failed during processing. Check the error message for details.
Checking Job Status
Use client.queue.status() to check the current state of a job:
import { createDecartClient } from "@decart/sdk" ;
const client = createDecartClient ({
apiKey: process . env . DECART_API_KEY ,
});
const status = await client . queue . status ( "job_abc123" );
console . log ( "Job ID:" , status . job_id );
console . log ( "Status:" , status . status );
Response Type
type JobStatusResponse = {
job_id : string ; // Unique job identifier
status : JobStatus ; // Current status
};
Retrieving Results
Once a job reaches "completed" status, retrieve the result:
const status = await client . queue . status ( jobId );
if ( status . status === "completed" ) {
// Download the video
const videoBlob = await client . queue . result ( jobId );
// Save to file (Node.js)
const arrayBuffer = await videoBlob . arrayBuffer ();
await fs . writeFile ( "output.mp4" , Buffer . from ( arrayBuffer ));
// Or create object URL (browser)
const url = URL . createObjectURL ( videoBlob );
videoElement . src = url ;
}
Only call client.queue.result() when the job status is "completed". Calling it on incomplete jobs will result in an error.
Error Handling
Failed Jobs
When using submitAndPoll(), check the result status:
const result = await client . queue . submitAndPoll ({
model: models . video ( "lucy-pro-t2v" ),
prompt: "A beautiful sunset" ,
});
if ( result . status === "completed" ) {
console . log ( "Success! Video size:" , result . data . size , "bytes" );
// Process the video blob
displayVideo ( result . data );
} else if ( result . status === "failed" ) {
console . error ( "Job failed:" , result . error );
// Show error to user
showError ( `Video generation failed: ${ result . error } ` );
}
Manual Status Checks
When polling manually:
const job = await client . queue . submit ({
model: models . video ( "lucy-pro-t2v" ),
prompt: "Mountain landscape" ,
});
let status = job . status ;
while ( status === "pending" || status === "processing" ) {
await new Promise ( resolve => setTimeout ( resolve , 1500 ));
const statusResponse = await client . queue . status ( job . job_id );
status = statusResponse . status ;
}
if ( status === "completed" ) {
const video = await client . queue . result ( job . job_id );
console . log ( "Success!" );
} else if ( status === "failed" ) {
console . error ( "Job failed" );
// Implement retry logic, notify user, etc.
}
Validation Errors
Input validation errors occur before job submission:
try {
const job = await client . queue . submit ({
model: models . video ( "lucy-pro-t2v" ),
prompt: "" , // Invalid: empty string
});
} catch ( error ) {
console . error ( "Validation error:" , error . message );
// Example output:
// "Invalid inputs for lucy-pro-t2v: String must contain at least 1 character(s)"
}
Common validation errors:
Empty or too long prompts (max 1000 characters)
Invalid file inputs (wrong format, inaccessible URL)
Missing required parameters
Invalid parameter combinations (e.g., both prompt and reference_image for lucy-restyle-v2v)
Network Errors
Handle network and API errors:
try {
const result = await client . queue . submitAndPoll ({
model: models . video ( "lucy-pro-t2v" ),
prompt: "Beautiful landscape" ,
});
if ( result . status === "completed" ) {
handleSuccess ( result . data );
} else {
handleJobFailure ( result . error );
}
} catch ( error ) {
// Network error, auth error, or other exception
console . error ( "Request failed:" , error . message );
if ( error . message . includes ( "401" )) {
showError ( "Invalid API key" );
} else if ( error . message . includes ( "network" )) {
showError ( "Network error - please check your connection" );
} else {
showError ( "An unexpected error occurred" );
}
}
Comprehensive Error Handling Example
import { createDecartClient , models } from "@decart/sdk" ;
const client = createDecartClient ({
apiKey: process . env . DECART_API_KEY ,
});
async function generateVideo ( prompt : string ) {
try {
console . log ( "Submitting job..." );
const result = await client . queue . submitAndPoll ({
model: models . video ( "lucy-pro-t2v" ),
prompt ,
onStatusChange : ( job ) => {
console . log ( `[ ${ job . job_id } ] Status: ${ job . status } ` );
// Update UI based on status
if ( job . status === "pending" ) {
showMessage ( "Job queued, waiting to start..." );
} else if ( job . status === "processing" ) {
showMessage ( "Generating video..." );
}
},
});
// Check result
if ( result . status === "completed" ) {
console . log ( "✓ Video generated successfully" );
console . log ( " Size:" , result . data . size , "bytes" );
console . log ( " Type:" , result . data . type );
// Save or display the video
const arrayBuffer = await result . data . arrayBuffer ();
await fs . writeFile ( "output.mp4" , Buffer . from ( arrayBuffer ));
return { success: true , video: result . data };
} else {
console . error ( "✗ Job failed:" , result . error );
console . error ( " Job ID:" , result . job_id );
return { success: false , error: result . error };
}
} catch ( error ) {
// Handle validation and network errors
console . error ( "✗ Request failed:" , error . message );
if ( error . message . includes ( "Invalid inputs" )) {
console . error ( " Fix your input parameters" );
} else if ( error . message === "Polling aborted" ) {
console . error ( " Request was cancelled" );
} else {
console . error ( " Check your network connection and API key" );
}
return { success: false , error: error . message };
}
}
// Usage
const result = await generateVideo ( "A serene mountain lake at dawn" );
if ( result . success ) {
console . log ( "Video is ready!" );
} else {
console . log ( "Failed to generate video:" , result . error );
}
Timeout Behavior
The backend automatically fails jobs that take longer than 10 minutes. When a job times out,
its status changes to "failed". You don’t need to implement your own timeout logic.
If you want to cancel polling before the backend timeout:
const controller = new AbortController ();
// Cancel after 5 minutes
setTimeout (() => controller . abort (), 5 * 60 * 1000 );
try {
const result = await client . queue . submitAndPoll ({
model: models . video ( "lucy-pro-t2v" ),
prompt: "Complex scene with many elements" ,
signal: controller . signal ,
});
} catch ( error ) {
if ( error . message === "Polling aborted" ) {
console . log ( "Cancelled polling after 5 minutes" );
}
}
Retry Strategies
Implement retry logic for failed jobs:
async function generateVideoWithRetry ( prompt : string , maxRetries = 3 ) {
for ( let attempt = 1 ; attempt <= maxRetries ; attempt ++ ) {
console . log ( `Attempt ${ attempt } / ${ maxRetries } ` );
const result = await client . queue . submitAndPoll ({
model: models . video ( "lucy-pro-t2v" ),
prompt ,
});
if ( result . status === "completed" ) {
return { success: true , data: result . data };
}
console . warn ( `Attempt ${ attempt } failed:` , result . error );
// Wait before retrying (exponential backoff)
if ( attempt < maxRetries ) {
const delay = Math . pow ( 2 , attempt ) * 1000 ; // 2s, 4s, 8s
console . log ( `Waiting ${ delay } ms before retry...` );
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
}
return { success: false , error: "Max retries exceeded" };
}
// Usage
const result = await generateVideoWithRetry ( "A magical forest scene" );
if ( result . success ) {
console . log ( "Success after retries!" );
}
Next Steps
Overview Review Queue API concepts and workflow
Submit Jobs Learn how to submit jobs for different models