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.
- Test a webhook endpoint before go-live: a Sume CI gate (Python)
Use POST /v1/webhooks/test-deliveries to fire a signed webhook.test at your deployed URL and fail the deploy unless it answers 2xx. Python script included.
- Four avatar clips a week: Python batch, one idempotency key each
HeyGen's survey ties avatars to consistent posting. Submit four Sume avatar clips a week from one handle with week-stamped keys and queue_full handling.
- What to measure for Sume API jobs: metrics, labels and alerts
A metrics plan for code that calls the Sume API: submit outcome, queue wait, time to terminal, error code and webhook gap, with low-cardinality labels.
Written by Sume