Node 26.11 ships Undici 8.11.2: tell a Sume poll timeout from an abort
Catch TimeoutError separately from AbortError when a Sume status poll in Node fetch times out, and know which Sume calls are safe to repeat after that timeout.

When a Sume status poll times out in Node, fetch rejects and the error's name tells you why: a deadline you set with AbortSignal.timeout() rejects with TimeoutError, while a signal that a caller aborted rejects with AbortError. Handle them separately. A poll timeout means ask again; a caller abort means stop. In a test on Node 22.14 a silent local server produced TimeoutError after the set delay, and a closed port produced a network error whose cause.code was ECONNREFUSED.
This week's Node.js 26.11.0 notes list Undici 8.11.2 among the updated dependencies, so Node 26 users are on a new fetch implementation. The pattern below does not depend on the version, but it is a good moment to add the test.
Which Sume calls are safe after a timeout
A timeout tells you only that you did not see a response. It does not tell you the server did nothing. For read calls that is harmless. For a paid submit it is the whole problem, which is why Sume asks you to repeat the submit with the same Idempotency-Key instead of sending a new job. The jobs and results docs describe these routes.
| Route | Method | After a timeout |
|---|---|---|
| /v1/jobs/{id}/status | GET | Safe to repeat; honor next_poll_after_seconds |
| /v1/jobs/{id}/result | GET | Safe to repeat; 409 job_not_completed means not finished yet |
| /v1/images or /v1/videos submit | POST | Repeat with the same Idempotency-Key, never a new one |
| /v1/jobs/{id}/cancel | POST | Check status first; cancel works only before generation starts |
Steps
Treat the per-request timeout and the overall job deadline as two different numbers. Video jobs can take minutes, but a single status read should answer in seconds. If one read hangs, you want to drop that connection quickly and ask again, while the clock for the whole job keeps ticking. Mixing the two is how a loop either gives up too early or waits forever on a dead socket.
- Give every poll its own
AbortSignal.timeout(), short enough that one stuck connection cannot eat your whole job deadline. - Branch on
err.name. Return a retry hint forTimeoutError, stop forAbortError, and readerr.cause?.codefor network failures. - Keep a total deadline separate from the per-request one. A poll timeout should consume deadline, not reset it.
- Authenticate with one credential. Sending both
Authorization: Bearerandx-api-keyreturns a 401, as the authentication docs explain.
Sample
The function below returns a small result object instead of throwing, so a poll loop can decide what to do. It reads the key from SUME_API_KEY.
const base = process.env.SUME_BASE ?? "https://api.sume.com";
const auth = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };
async function getStatus(jobId, ms = 10_000) {
try {
const res = await fetch(`${base}/v1/jobs/${jobId}/status`, {
headers: auth,
signal: AbortSignal.timeout(ms),
});
return { ok: res.ok, body: await res.json() };
} catch (err) {
if (err.name === "TimeoutError") return { retry: "poll timed out, safe to ask again" };
if (err.name === "AbortError") return { retry: "aborted by a caller signal" };
return { retry: `network error: ${err.cause?.code ?? err.message}` };
}
}
const quick = await getStatus("job_demo", Number(process.env.POLL_MS ?? 10_000));
console.log(JSON.stringify(quick));What Sume does not do
Sume does not return a special status when your client gives up. The job keeps running and keeps its status, so after a timeout you ask again by job id. Sume also does not cancel a job because your poll was aborted; cancel is a separate call that works only before generation starts.
Sources
Related posts
More in Developers
- Node 26 enters LTS in October 2026: pin your Sume webhook receiver
Node 26 moves to LTS this month and Node 27 Alpha starts. Pin the runtime that runs your Sume webhook receiver and prove it with a signed self-test.
- node:test mock.method on fetch: test a Sume status poll offline
Node 26 goes LTS this month. Test a Sume job status poll with node:test and mock.method on fetch: four cases, no network, no key, no third-party test library.
- npm audit signatures on @sume-com/sdk 0.2.0: what it proves, what not
Run npm audit signatures on @sume-com/sdk 0.2.0 before deploy. It checks the registry signature; the registry lists no provenance attestation for this version.
- Omni Flash API errors: which to retry and which to fix
A retry policy for Gemini Omni Flash 1.1 calls on Sume: 400, 401, 402, 409, 429 rate_limited, 429 queue_full and 503 each mapped to retry, wait or fix.
Written by Sume