Node 26.11 --process-timeout exits 124: the Sume job keeps running
Node 26.11.0 adds --process-timeout, which kills a script with exit code 124. A Sume job it was waiting on keeps running and billing, so save the polling URL.

Node 26.11.0 adds --process-timeout=duration, which ends a process with exit code 124 once the duration has passed. For a script that polls a Sume job, exit 124 means you stopped waiting, not that the job stopped. The job keeps running and keeps billing, so print the polling_url before the first poll and resume from it instead of submitting again.
What the flag does
The details below are from the Node.js v26.11.0 CLI docs (read 2026-10-10) and the release notes.
| Property | What the docs say |
|---|---|
| Exit code | 124, the same code timeout(1) uses |
| Clock | Measured from the start of the process |
| Duration format | A positive integer plus ms, s, m, or h, such as 500ms or 5m |
| Events | The process exits without beforeExit or exit; coverage and profiles are still written |
| Diagnostics | Prints the main-thread stack or the resources keeping the loop alive to stderr; --report-on-process-timeout adds a diagnostic report |
| Where it works | Not allowed in NODE_OPTIONS; cannot be combined with the inspector flags |
| Stability | 1.1, active development |
Why a Sume poll script needs a plan for 124
Sume's jobs page is direct about this case: a client-side timeout does not cancel the job, and you should not submit a paid request again only because a local process timed out. A hard kill with no exit handlers makes the mistake easy, because nothing in the dying process can save state for you.
So the script writes what a restart needs before it waits. It submits with a stable Idempotency-Key, prints the polling_url from the video API response, and accepts that URL as an argument on the next run. Run it as node --process-timeout=10m poll.mjs.
const headers = {
Authorization: `Bearer ${process.env.SUME_API_KEY}`,
"Content-Type": "application/json",
};
let url = process.argv[2]; // pass the saved polling_url to resume
if (!url) {
if (!process.env.RUN_KEY) throw new Error("set RUN_KEY, one stable key per render");
const res = await fetch("https://api.sume.com/v1/videos", {
method: "POST",
headers: { ...headers, "Idempotency-Key": process.env.RUN_KEY },
body: JSON.stringify({ model: "seedance-2.5", prompt: "Rain on a neon street", duration: 5 }),
});
url = (await res.json()).polling_url;
console.log(`RESUME: node poll.mjs ${url}`); // 'exit' handlers do not run on 124
}
for (;;) {
const s = await (await fetch(url, { headers })).json();
if (s.status === "completed") {
console.log(s.unsigned_urls[0]);
break;
}
if (s.status === "failed") {
console.error(JSON.stringify(s.error));
process.exit(1);
}
await new Promise((r) => setTimeout(r, 30_000));
}Handle the exit code in CI
A shell step can tell the three outcomes apart. Exit 0 means the clip URL was printed. Exit 1 means the job reached failed. Exit 124 means the job is probably still running, so the step should fail soft and keep the RESUME: line as an artifact or a log annotation.
- Do not retry the whole step on 124 with a new run key. A new key is a new job and a second charge.
- Do rerun with the same
RUN_KEYif you lost the URL. The submit returns the original job because the key matches. - Pick a duration longer than the job normally takes. The
seedance-2.5row accepts up to 30 seconds of video, and queue time comes on top. - Keep the flag on the command line or in an npm script. Node rejects it inside
NODE_OPTIONS.
The flag is a good safety net for a runaway loop. It is not a substitute for deciding what a timeout means for work that is already paid for.
Sources
Related posts
More in Developers
- Node script for a 9:16 TikTok video: check the model, then submit
A Node 20 fetch script that confirms a Sume model lists 9:16 and your duration, submits one 12-second 720p job, and saves an MP4 that fits TikTok's API limits.
- Test a Sume poll loop with node:test mock.timers, no real sleeping
Use node:test mock.timers to prove your poll loop waits next_poll_after_seconds, falls back to 2 s, and never polls early. A runnable test, no network.
- One length gate for six ad platforms: caps table and Sume plan
Reels 15 min, Stories 60 min, TikTok 10 min, LinkedIn 30 min, Pinterest 5 min, Snap 180 s. Check one Sume Timeline length against all six with the plan call.
- openapi-fetch with Sume's OpenAPI JSON: a typed client in 20 lines
Generate types from Sume's reference JSON with openapi-typescript, then call it with openapi-fetch and an x-api-key middleware. A type-checked sample.
Written by Sume