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.

4 min readSume
All posts

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.

--process-timeout in Node.js v26.11.0 docs, read 2026-10-10
PropertyWhat the docs say
Exit code124, the same code timeout(1) uses
ClockMeasured from the start of the process
Duration formatA positive integer plus ms, s, m, or h, such as 500ms or 5m
EventsThe process exits without beforeExit or exit; coverage and profiles are still written
DiagnosticsPrints the main-thread stack or the resources keeping the loop alive to stderr; --report-on-process-timeout adds a diagnostic report
Where it worksNot allowed in NODE_OPTIONS; cannot be combined with the inspector flags
Stability1.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_KEY if 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.5 row 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

All Developers posts

Written by Sume