waitForRun timeout: why SumeRunTimeoutError can arrive early
The Sume SDK's waitForRun checks its deadline before it sleeps, so SumeRunTimeoutError can fire up to one poll interval early. The run keeps going.

waitForRun in the Sume TypeScript SDK checks its deadline before it sleeps. If another poll interval would cross the deadline, it throws SumeRunTimeoutError right away instead of sleeping through it, so the error can arrive up to one poll interval before the timeout you passed. The run itself keeps going.
That behavior is described in Waiting for runs and jobs and visible in the SDK source. It matters if you assert on elapsed time in tests, or if you set a timeout close to your poll interval.
What exactly does the deadline check do?
On entry the helper computes deadline = now + timeout. After each non-terminal status read it asks one question: would the next sleep, now + pollInterval, reach the deadline? If yes it throws SumeRunTimeoutError carrying the runId and lastStatus. If not it sleeps with jitter and reads again.
The docs put the intent plainly: the deadline is checked before sleeping, not after, so a caller who asks for a 5 second timeout hears about it in about 5 seconds, rather than 5 seconds plus one whole poll interval. The cost of that choice is that the final window shorter than one interval is never used for another read.
| Option | Default | Effect |
|---|---|---|
| timeout | 10 minutes | Deadline for the whole wait |
| pollInterval | 2 seconds | Gap between status reads; also the early-exit window |
| signal | none | Aborts the wait and the in-flight request; rejects with the signal's reason |
| maxTransientFailures | 6 | Consecutive failed reads tolerated |
What does a short timeout look like?
With timeout: 5000 and the default 2 second interval, the helper reads, sleeps about two seconds, reads again, and so on. The read at roughly the four second mark sees that another sleep would pass the five second deadline, so it throws then. A timeout shorter than one interval throws after the first non-terminal read.
The snippet below shows the pattern with a run id you already hold. It catches the timeout and stores the last status instead of treating it as a failure.
import { createSumeClient, waitForRun, SumeRunTimeoutError } from "@sume-com/sdk";
const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });
export async function check(runId: string) {
try {
return await waitForRun(runId, {
client,
family: "format",
timeout: 5_000,
pollInterval: 2_000,
});
} catch (error) {
if (error instanceof SumeRunTimeoutError) {
// Still running. Nothing was cancelled.
return { pending: true, runId: error.runId, lastStatus: error.lastStatus };
}
throw error;
}
}Does a timeout stop the run?
No. The docs repeat it in two places: a timeout does not cancel the run, which keeps going and keeps spending, and you have only stopped watching. Store the run id and read it later with getFormatRun, or cancel it with cancelFormatRun.
For a Format run the enforced deadline lives on the receipt as expires_at, so a better wait budget comes from that field than from a guess. The SDK's own comments say to use it to bound your own wait instead of inventing a timeout.
What about abort signals?
A signal aborts the wait and the in-flight request and rejects with the signal's reason, not with SumeRunTimeoutError. Branch on both: the timeout error means the budget ran out, an abort means your own code stopped. Neither says anything about the run's real status.
If you also want a hard cap beyond the helper's own deadline, pass AbortSignal.timeout(...) as in the docs example, and keep it longer than timeout so the helper's cleaner error wins.
Which helper has which default?
waitForRun defaults to 10 minutes. subscribeFormatRun, which creates and then waits, defaults to 20 minutes because video Formats routinely run 10 to 20. Pick a timeout from the Format's real runtime, and keep the poll interval at or below a fifth of it so the early exit costs you little.
Sources
Related posts
More in Developers
- waitForRun 429 and 503: the streak resets only on a clean read
Sume SDK waitForRun counts consecutive failed reads, resets only on a clean one, and retries the final result read so a late 429 cannot lose it.
- How much balance does a full Sume queue hold? By plan
A full Pro queue of 24 Seedance 2.5 clips at 720p holds $208 of wallet balance; Scale's 120 holds $1,040. Reserve by plan.
- Walmart Sponsored Videos: 15-minute upload URL, wait for AVAILABLE
Walmart's Sponsored Videos API gives an upload URL that expires in 15 minutes and needs about 30 minutes to process. How to stage a Sume clip for it safely.
- WAV or MP3 for AI narration you will trim or join: Sume defaults
Sume TTS defaults to mp3 at 44.1 kHz and 128 kbps, but Timeline audio says mp3 re-adds priming padding at every edge. Use wav for narration you cut or join.
Written by Sume