Observation-class finding, no behaviour change proposed. Found while drafting the upstream tsup report on 12167 (the guard's header is the repo's most accurate written description of the defect, so it is the text the next debugger will read).
What the guard says — in two places
- Header comment, in the block titled
THE DEFECT IT EXISTS TO CLOSE:
If the worker dies without posting a message -- OOM under memory pressure, a hard process.exit, a terminated thread -- neither branch ever runs, the promise NEVER SETTLES, the event loop drains, and Node exits 0.
- The failure message the guard prints to the engineer who trips it (same file, the
console.error block):
A worker that dies (OOM under memory pressure is the observed one) posts neither "success" nor "error" [...]
Re-run the build. If it keeps failing here, build with more headroom:
NODE_OPTIONS=--max-old-space-size=8192 pnpm --filter PKG build
The same attribution is carried in 12167's body. Both quotes verified against origin/main, not a working tree.
What is actually measured
Node v22.22.2, Linux x64, four runs of a harness that mirrors tsup's promise structure exactly (only worker.on('message') registered):
| Worker death mode | Event that fires | Process outcome |
|---|
process.exit() inside the worker, CJS caller | 'exit' only | exit 0, zero output — the silent false-success class |
process.exit() inside the worker, ESM caller with top-level await | 'exit' only | exit 13, Warning: Detected unsettled top-level await |
Heap OOM, per-worker resourceLimits: { maxOldGenerationSizeMb: 16 } | 'error'ERR_WORKER_OUT_OF_MEMORY | exit 1, Unhandled 'error' event |
Heap OOM, process-wide --max-old-space-size=64, no resourceLimits | 'error'ERR_WORKER_OUT_OF_MEMORY | exit 1, Unhandled 'error' event |
Node has emitted ERR_WORKER_OUT_OF_MEMORY as an 'error' event for many major versions, and an 'error' event with no listener is rethrown by EventEmitter — so an OOM'd DTS worker fails loudly and non-zero. A non-zero build is never cached by turbo, which is precisely the property the header says OOM lacks.
The silent exit-0 shape requires a worker that ends without an 'error' event: process.exit() reached inside the worker, a terminated thread, a thread that ends message-less for any other reason. The caller's module system is load-bearing too: the same message-less death exits 13 with a warning from an ESM top-level await, and 0 in silence from CJS. tsup's CLI is CJS, which is why the observed shape was silent.
Why it is worth correcting
Two distinct costs, and the second is the sharper one:
- The header is written as a debugging aid and is cited from 12167. Someone who hits a declaration-less
dist/ will follow it to "we were memory-starved", try to reproduce under memory pressure, get a loud crash, and conclude the guard's premise is wrong. - The printed remediation tells the engineer who trips the guard to retry with
--max-old-space-size=8192. On the measurement above, that is advice for the path that does not produce this failure. Raising the heap cannot be shown to address a message-less exit, so the guidance is likely to send someone through a retry loop that never converges — while the genuinely useful half of that message (the turbo --force cache-clearing step) sits below it.
The correct lede is the class, not the trigger: any DTS worker death that posts no message leaves the promise pending, and from a CJS caller that is exit 0.
Note what is not claimed here: this does not identify what actually killed the worker in the original plugin-auth observation. It establishes only that whatever killed it ended the thread without an 'error' event, because the run exited 0 — and that memory pressure is not a demonstrated cause of that shape.
Suggested change (docs and one printed message; no logic)
Reword the header sentences and the printed "Most likely cause" paragraph to name the class first, list OOM as the loud sibling rather than the observed shape, and stop leading the remediation with the heap-size retry. The guard's logic, its self-test, and its rollout are all unaffected — declared declaration paths must exist and be non-empty regardless of what killed the worker.
Left unassigned and unfixed deliberately: the round that found it was a drafting task with an explicit report-only scope.
Observation-class finding, no behaviour change proposed. Found while drafting the upstream tsup report on 12167 (the guard's header is the repo's most accurate written description of the defect, so it is the text the next debugger will read).
What the guard says — in two places
THE DEFECT IT EXISTS TO CLOSE:console.errorblock):The same attribution is carried in 12167's body. Both quotes verified against
origin/main, not a working tree.What is actually measured
Node v22.22.2, Linux x64, four runs of a harness that mirrors tsup's promise structure exactly (only
worker.on('message')registered):process.exit()inside the worker, CJS caller'exit'onlyprocess.exit()inside the worker, ESM caller with top-level await'exit'onlyWarning: Detected unsettled top-level awaitresourceLimits: { maxOldGenerationSizeMb: 16 }'error'ERR_WORKER_OUT_OF_MEMORYUnhandled 'error' event--max-old-space-size=64, noresourceLimits'error'ERR_WORKER_OUT_OF_MEMORYUnhandled 'error' eventNode has emitted
ERR_WORKER_OUT_OF_MEMORYas an'error'event for many major versions, and an'error'event with no listener is rethrown byEventEmitter— so an OOM'd DTS worker fails loudly and non-zero. A non-zero build is never cached by turbo, which is precisely the property the header says OOM lacks.The silent exit-0 shape requires a worker that ends without an
'error'event:process.exit()reached inside the worker, a terminated thread, a thread that ends message-less for any other reason. The caller's module system is load-bearing too: the same message-less death exits 13 with a warning from an ESM top-levelawait, and 0 in silence from CJS. tsup's CLI is CJS, which is why the observed shape was silent.Why it is worth correcting
Two distinct costs, and the second is the sharper one:
dist/will follow it to "we were memory-starved", try to reproduce under memory pressure, get a loud crash, and conclude the guard's premise is wrong.--max-old-space-size=8192. On the measurement above, that is advice for the path that does not produce this failure. Raising the heap cannot be shown to address a message-less exit, so the guidance is likely to send someone through a retry loop that never converges — while the genuinely useful half of that message (theturbo --forcecache-clearing step) sits below it.The correct lede is the class, not the trigger: any DTS worker death that posts no message leaves the promise pending, and from a CJS caller that is exit 0.
Note what is not claimed here: this does not identify what actually killed the worker in the original plugin-auth observation. It establishes only that whatever killed it ended the thread without an
'error'event, because the run exited 0 — and that memory pressure is not a demonstrated cause of that shape.Suggested change (docs and one printed message; no logic)
Reword the header sentences and the printed "Most likely cause" paragraph to name the class first, list OOM as the loud sibling rather than the observed shape, and stop leading the remediation with the heap-size retry. The guard's logic, its self-test, and its rollout are all unaffected — declared declaration paths must exist and be non-empty regardless of what killed the worker.
Left unassigned and unfixed deliberately: the round that found it was a drafting task with an explicit report-only scope.