waitForJob resolves for failed jobs: read job.status, not catch (TS)

In the Sume SDK on main, waitForJob returns the job for completed, failed and canceled alike. Branch on job.status and job.error, and keep catch for timeouts.

4 min readSume
All posts

A common first draft of a job wrapper puts waitForJob in a try block and treats a throw as "the video failed". In the Sume SDK source on main that reading is wrong. waitForJob resolves with the final job record for any terminal status: completed, failed and canceled. A failed generation is a result you asked for, so it arrives as a return value with status and error fields, exactly as a webhook handler would see it.

The helper is on main only. The published @sume-com/sdk 0.2.0 package, dated 2026-08-02, does not export it, so use a build from main for this sample, or use a hand-written poll loop on 0.2.0.

What throws and what returns

waitForJob outcomes in the Sume SDK source on main (read 2026-10-03)
SituationResult
Job completedResolves with the job, status completed
Job failed or canceledResolves with the job, error filled in for a failure
Client timeout elapses firstThrows SumeJobTimeoutError with jobId and lastStatus
Status or job read returns an errorThrows SumeJobRequestError with the HTTP status and body
Your AbortSignal firesThe signal's reason is thrown and the request is aborted

A wait with progress and a deadline

The sample reads the job id from the command line, polls at least every 3 seconds and logs each status snapshot through onStatus. The snapshot is the whole status payload. next_poll_after_seconds from the server can only raise the gap between polls, never shorten it. Two deadlines are set: timeout makes the SDK throw SumeJobTimeoutError, and the AbortSignal is a backstop that also cancels the request in flight.

import { createSumeClient, waitForJob } from "@sume-com/sdk";

const client = createSumeClient({ apiKey: process.env.SUME_API_KEY ?? "" });

const job = await waitForJob(process.argv[2] ?? "job_f", {
  client,
  timeout: 10 * 60_000,
  pollInterval: 3_000,
  signal: AbortSignal.timeout(11 * 60_000),
  onStatus: (status, snapshot) =>
    console.log(status, snapshot.next_action ?? "-", snapshot.queue?.state ?? "-"),
});

if (job.status === "completed") {
  console.log("done", job.id);
} else {
  // A failed or canceled job resolves here. It is not an exception.
  console.log(job.status, job.error?.code, job.error?.retryable, job.error?.next_action);
}

Reading the result

  • Branch on job.status. Only completed has a usable result. For anything else read job.error, which carries code, retryable and next_action.
  • The final hop reads the job record, not the result route, because GET /v1/jobs/{id}/result answers 409 job_not_completed for failed and canceled jobs.
  • A timeout does not stop the job. It keeps running and billing, so keep the id and poll again, or cancel it if it has not started.
  • Do not resubmit on a timeout. A resubmit with the same Idempotency-Key returns the original job instead of a second charge.

The polling contract is described in the jobs guide, and client options are in the SDK guide. Check the published package on npm before relying on a helper.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume