{"record":{"id":"9ebdd8d2bfe60739","repo":"stablyai/orca","slug":"plain-node-entry-guard-could-not-smoke-load-daem","errorCode":null,"errorMessage":"[plain-node-entry-guard] could not smoke-load daemon-entry.js under plain Node: ${result.error.message}","messagePattern":"\\[plain-node-entry-guard\\] could not smoke-load daemon-entry\\.js under plain Node: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/build-plugins/plain-node-entry-guard.ts","lineNumber":194,"sourceCode":"    child.on('error', (error: Error) => finish(null, null, error))\n    // 'close' gives the full stderr; 'exit' is the fallback so a held-open pipe\n    // cannot outlast the process itself.\n    child.on('close', (status, signal) => finish(status, signal))\n    child.on('exit', (status, signal) => {\n      drainTimer = setTimeout(() => finish(status, signal), SMOKE_STDERR_DRAIN_MS)\n    })\n  })\n}\n\n// Why: proves the whole daemon-entry graph resolves under plain Node (no\n// unresolved requires). require(\"electron\") does not throw in a dev tree with\n// node_modules present, so the static scan above — not this smoke — is the\n// electron regression guard; this only catches gross load failures.\nasync function smokeLoadDaemonEntry(outputDir: string, timings: SmokeTimings): Promise<void> {\n  const entryPath = join(outputDir, 'daemon-entry.js')\n  const result = await runDaemonEntry(entryPath, timings)\n  if (result.error) {\n    throw new Error(\n      `[plain-node-entry-guard] could not smoke-load daemon-entry.js under plain Node: ` +\n        `${result.error.message}`\n    )\n  }\n  // Almost always means the daemon stopped rejecting an empty argv and started\n  // listening instead.\n  if (result.timedOut) {\n    throw new Error(\n      `[plain-node-entry-guard] daemon-entry.js did not exit within ${timings.timeoutMs}ms on an ` +\n        `empty argv under plain Node, so the smoke killed it.`\n    )\n  }\n  if (result.signal) {\n    throw new Error(\n      `[plain-node-entry-guard] daemon-entry.js was killed by ${result.signal} under plain Node.`\n    )\n  }\n  const stderr = result.stderr","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/build-plugins/plain-node-entry-guard.ts#L176-L212","documentation":"During the closeBundle phase, the plugin smoke-loads `daemon-entry.js` under plain Node (via `spawn(process.execPath, [entryPath])`). If the spawn itself emits an 'error' event (child.on('error')), this guard wraps that error. This means the process could not be spawned at all — typically a missing executable, a missing entry file, or a permissions failure — not a runtime crash inside the daemon.","triggerScenarios":"The daemon-entry.js file does not exist at the expected `outputDir/daemon-entry.js` path (e.g., the build produced output to a different directory). The Node executable (`process.execPath`) is missing or not executable. An EACCES or ENOENT error during `spawn()`. This fires from `runDaemonEntry` at line 176 before any stderr or exit code is available.","commonSituations":"The output directory passed to `closeBundle` doesn't contain daemon-entry.js (rollup output config mismatch). Running on a CI runner where the Node binary is at an unexpected path. Filesystem permission errors on the build output directory.","solutions":["Check the `result.error.message` in the error output — an ENOENT means the entry file or Node binary is missing; an EACCES means a permissions issue.","Verify that `daemonOutputDir` was set correctly: the plugin sets it only when `entryByName.has('daemon-entry') && options.dir` in writeBundle (line 268). If the daemon entry wasn't in the bundle, the smoke is skipped entirely — so this error means the entry existed but the file couldn't be spawned.","Ensure the build output directory is writable and the daemon-entry.js file exists at `join(outputDir, 'daemon-entry.js')` before closeBundle runs."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Verify the daemon entry exists before the build's closeBundle phase\nimport { existsSync } from 'fs'\nimport { join } from 'path'\nfunction assertDaemonEntryExists(outputDir) {\n  const entryPath = join(outputDir, 'daemon-entry.js')\n  if (!existsSync(entryPath)) {\n    throw new Error(`daemon-entry.js not found at ${entryPath} — check rollup output config`)\n  }\n}","typeGuard":null,"tryCatchPattern":"// The spawn error is environmental; wrap in a diagnostic message\ntry {\n  await smokeLoadDaemonEntry(outputDir, timings)\n} catch (error) {\n  if (error.message.includes('could not smoke-load')) {\n    console.error('Spawn failed — check Node binary and file permissions:', error.message)\n    console.error('Entry path:', join(outputDir, 'daemon-entry.js'))\n  }\n  throw error\n}","preventionTips":["Ensure the rollup output directory contains daemon-entry.js before the packaging step.","Verify process.execPath points to a valid Node binary in CI environments.","Check filesystem permissions on the build output directory."],"tags":["build-config","spawn","smoke-test","daemon","filesystem"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}