Skip to content

Commit 32e88de

Browse files
schmonzclaude
andcommitted
fix(naude): the self-check must not call a timeout a crash
Seen while proving the new attest gate: on a box merely busy with another build, `build-naude.mjs`'s own self-check printed naude self-check FAILED (crashed with SIGTERM): ... did not boot the baked bundle spawnSync reports a TIMEOUT kill as `signal: 'SIGTERM'` with `error.code ETIMEDOUT`, and the old wording collapsed the two — sending a reader hunting for the stripped-node SIGSEGV the very next lines describe, for a binary that was fine and merely slow. A naude's FIRST boot untars ~12MB of deps and writes a ~50MB cli.cjs before it can print anything. Now it names which one happened, and the 120s budget honours CLODE_TIMEOUT_SCALE — the same knob clode-fuse.cjs's SCALE already reads. Same class as killed-is-not-exit-zero: a diagnostic that misnames the failure costs more than no diagnostic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WMm6zrQy4L7zQYRD1esZko
1 parent 41d9831 commit 32e88de

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

‎scripts/build-naude.mjs‎

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -488,17 +488,31 @@ export async function buildBinary({
488488
// materializes the deps + assets, re-invokes as node, and runs the baked cli.cjs, which
489489
// prints Claude Code's version and exits 0. A stripped-node SIGSEGV or a corrupt deps
490490
// asset both surface here.
491+
// The self-check's budget. A naude's FIRST boot untars ~12MB of deps and writes a ~50MB
492+
// cli.cjs before it can print anything, so on a loaded machine this is minutes, not
493+
// seconds. CLODE_TIMEOUT_SCALE is the same knob clode-fuse.cjs's SCALE reads.
494+
const SELFCHECK_TIMEOUT_MS = 120000 * (Number(process.env.CLODE_TIMEOUT_SCALE) || 1);
495+
491496
function smokeCheck(bin) {
492497
const cache = fs.mkdtempSync(path.join(os.tmpdir(), 'naude-selfcheck-'));
493498
try {
494499
const r = spawnSync(bin, ['--version'], {
495-
encoding: 'utf8', timeout: 120000,
500+
encoding: 'utf8', timeout: SELFCHECK_TIMEOUT_MS,
496501
env: { ...process.env, NAUDE_CACHE: cache },
497502
});
498503
if (r.status !== 0 || /Cannot find module|MODULE_NOT_FOUND/.test(r.stderr || '')) {
499-
console.error(`naude self-check FAILED${r.signal ? ` (crashed with ${r.signal})` : ''}: ` +
500-
`'${bin} --version' did not boot the baked bundle.`);
501-
if (r.signal === 'SIGSEGV') {
504+
// TIMED OUT AND CRASHED ARE DIFFERENT BUGS, and spawnSync reports both as a signal:
505+
// a timeout kill arrives as SIGTERM with no `error`, which the old wording called
506+
// "crashed with SIGTERM" — sending a reader hunting for a startup crash that never
507+
// happened (this cost real time on 2026-08-30, on a box merely busy with another
508+
// build). Say which one it was.
509+
const timedOut = r.error && r.error.code === 'ETIMEDOUT';
510+
const how = timedOut
511+
? ` (TIMED OUT after ${SELFCHECK_TIMEOUT_MS}ms — a slow/loaded box, not necessarily a broken binary; raise CLODE_TIMEOUT_SCALE)`
512+
: (r.signal ? ` (crashed with ${r.signal})` : '');
513+
console.error(`naude self-check FAILED${how}: `
514+
+ `'${bin} --version' did not boot the baked bundle.`);
515+
if (!timedOut && r.signal === 'SIGSEGV') {
502516
console.error('A SIGSEGV at startup almost always means the embedded node was a STRIPPED');
503517
console.error('binary — postject corrupts stripped nodes and the loader crashes. Build with');
504518
console.error('an official, non-stripped Node (an asdf/nvm nodejs.org build), not a distro-');

0 commit comments

Comments
 (0)