stablyai/orca · critical
[plain-node-entry-guard] daemon-entry.js did not exit within
Error message
[plain-node-entry-guard] daemon-entry.js did not exit within ${timings.timeoutMs}ms on an empty argv under plain Node, so the smoke killed it. What it means
The smoke test spawns daemon-entry.js under plain Node with an empty argv and a 15-second timeout (DEFAULT_SMOKE_TIMINGS.timeoutMs = 15_000). If the process does not exit within that window, it is killed (SIGTERM then SIGKILL after killGraceMs) and this error fires. The comment notes this almost always means the daemon stopped rejecting an empty argv and started listening instead — i.e., the argv parser regressed to accept no arguments.
Source
Thrown at config/build-plugins/plain-node-entry-guard.ts:202
}
// Why: proves the whole daemon-entry graph resolves under plain Node (no
// unresolved requires). require("electron") does not throw in a dev tree with
// node_modules present, so the static scan above — not this smoke — is the
// electron regression guard; this only catches gross load failures.
async function smokeLoadDaemonEntry(outputDir: string, timings: SmokeTimings): Promise<void> {
const entryPath = join(outputDir, 'daemon-entry.js')
const result = await runDaemonEntry(entryPath, timings)
if (result.error) {
throw new Error(
`[plain-node-entry-guard] could not smoke-load daemon-entry.js under plain Node: ` +
`${result.error.message}`
)
}
// Almost always means the daemon stopped rejecting an empty argv and started
// listening instead.
if (result.timedOut) {
throw new Error(
`[plain-node-entry-guard] daemon-entry.js did not exit within ${timings.timeoutMs}ms on an ` +
`empty argv under plain Node, so the smoke killed it.`
)
}
if (result.signal) {
throw new Error(
`[plain-node-entry-guard] daemon-entry.js was killed by ${result.signal} under plain Node.`
)
}
const stderr = result.stderr
if (/Cannot find module|MODULE_NOT_FOUND/.test(stderr)) {
throw new Error(
`[plain-node-entry-guard] daemon-entry.js failed to load under plain Node:\n${stderr}`
)
}
if (result.status === 0 || !stderr.includes(DAEMON_USAGE_PREFIX)) {
throw new Error(
`[plain-node-entry-guard] daemon-entry.js did not reject an empty argv under plain Node ` +View on GitHub (pinned to 1136503c6a)
Solutions
- Check src/main/daemon/daemon-entry.ts — the argv parser must throw 'Usage: daemon-entry --socket <path> ...' and call process.exit(1) when required arguments are missing. Verify it still rejects empty argv.
- If argv validation is intact, look for new code added before the validation that blocks startup — move it after the argv check or behind a guard that only runs when arguments are present.
- If the timeout is too short for a slow CI machine, pass custom SmokeTimings to createPlainNodeEntryGuardPlugin — but only as a last resort; a hanging daemon is a real regression.
Example fix
// before — daemon-entry.ts made args optional (regression)
const socket = argv.socket ?? createDefaultSocket()
startDaemon(socket)
// after — keep rejecting empty argv
if (!argv.socket) {
throw new Error('Usage: daemon-entry --socket <path> --token <path> [--log-file <path>]')
} Defensive patterns
Strategy: validation
Validate before calling
// Verify daemon-entry rejects empty argv before running the full build
import { spawnSync } from 'child_process'
function verifyDaemonRejectsEmptyArgv(entryPath) {
const result = spawnSync(process.execPath, [entryPath], {
timeout: 5000, stdio: 'pipe'
})
if (result.status === 0) {
throw new Error('daemon-entry accepted empty argv — argv parser regressed')
}
} Prevention
- Always test argv parser changes in daemon-entry.ts with an empty-argv invocation before committing.
- Keep argv validation as the first thing daemon-entry does — before any initialization that could block.
- Never make required daemon arguments optional without updating the smoke test expectations.
When it happens
Trigger: The argv parser in src/main/daemon/daemon-entry.ts (line 85) stopped throwing on empty argv, so daemon-entry.js proceeds to start listening on a socket and never exits. The daemon's startup path gained a blocking operation (e.g., a server.listen() call) that runs before argv validation. A hung native module load (node-pty) that blocks the event loop.
Common situations: Modifying the daemon-entry argv parser to make arguments optional or add defaults. Adding initialization code before the argv check that opens a port or starts a long-running async operation. A dependency change that causes the daemon to block on startup.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- [plain-node-entry-guard] daemon-entry.js did not reject an e
- [plain-node-entry-guard] could not smoke-load daemon-entry.j
- [plain-node-entry-guard] daemon-entry.js was killed by ${res
- [plain-node-entry-guard] daemon-entry.js failed to load unde
- [plain-node-entry-guard] "${entryName}" reaches chunk "${chu
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/115ce9deed407874.
Report an issue: GitHub.