stablyai/orca · critical
[plain-node-entry-guard] "${entryName}" reaches chunk "${chu
Error message
[plain-node-entry-guard] "${entryName}" reaches chunk "${chunk.fileName}" that requires electron. "${entryName}" runs as a ${runtime}, where require("electron") throws MODULE_NOT_FOUND and kills it at startup (the v1.4.129-rc.1 daemon outage). Keep electron imports out of its module graph. What it means
The build plugin statically scans every chunk reachable from a plain-Node or worker-thread entry (via import/dynamicImport graph traversal) for `require("electron")`. The daemon, sidecars, and worker threads run without Electron's module registered, so `require("electron")` throws MODULE_NOT_FOUND and kills the process at startup — this was the root cause of the v1.4.129-rc.1 daemon outage. The guard fails the build so no chunk in those entry graphs transitively pulls in electron.
Source
Thrown at config/build-plugins/plain-node-entry-guard.ts:108
continue
}
reachable.push(chunk)
for (const imported of [...chunk.imports, ...chunk.dynamicImports]) {
stack.push(imported)
}
}
return reachable
}
function assertNoElectronRequire(
entryName: string,
entry: OutputChunk,
byFileName: Map<string, OutputChunk>,
runtime: EntryRuntime = 'plain-Node process'
): void {
for (const chunk of collectReachableChunks(entry, byFileName)) {
if (ELECTRON_REQUIRE_RE.test(chunk.code)) {
throw new Error(
`[plain-node-entry-guard] "${entryName}" reaches chunk "${chunk.fileName}" that ` +
`requires electron. "${entryName}" runs as a ${runtime}, where ` +
`require("electron") throws MODULE_NOT_FOUND and kills it at startup (the ` +
`v1.4.129-rc.1 daemon outage). Keep electron imports out of its module graph.`
)
}
}
}
// Owned by the argv parser in src/main/daemon/daemon-entry.ts — keep in sync.
const DAEMON_USAGE_PREFIX = 'Usage: daemon-entry'
export type SmokeTimings = {
timeoutMs: number
// daemon-entry traps SIGTERM and awaits a native shutdown, so the deadline
// needs an uncatchable follow-up to stay a deadline.
killGraceMs: number
}View on GitHub (pinned to 1136503c6a)
Solutions
- Read the error: it names the entry (`entryName`) and the offending chunk (`chunk.fileName`). Trace why that chunk is in the entry's import graph — run the build with `--logLevel debug` or inspect the chunk's imports.
- Break the import edge: extract the electron-free logic into a separate module that the plain-Node entry imports directly, or use a lazy `require("electron")` inside a function that the plain-Node path never calls (though the static regex will still flag it — you may need to gate with a runtime check instead).
- If the require is legitimately unreachable at runtime (dead code), restructure so the bundler tree-shakes it out of the chunk, or move the electron require behind a dynamic import that Rollup splits into a separate chunk not reachable from the guarded entry.
Example fix
// before — src/main/daemon/shared-utils.ts imports electron indirectly
const { app } = require('electron')
export function getDaemonPath() { return app.getPath('userData') }
// after — split into two modules
// src/main/daemon/shared-utils.ts (electron-free)
export function getDaemonPath(basePath: string) { return basePath }
// src/main/electron-utils.ts (electron-aware, not imported by daemon)
const { app } = require('electron')
export function getDaemonPath() { return app.getPath('userData') } Defensive patterns
Strategy: validation
Validate before calling
// Check a module's import graph for electron before adding it to a guarded entry
// Run: node -e "<script>" or use the ELECTRON_REQUIRE_RE from the guard
const ELECTRON_REQUIRE_RE = /require\(\s*["'`]electron(?:\/[^"'`]+)?["'`]\s*\)/
function assertNoElectron(source, path = '') {
if (ELECTRON_REQUIRE_RE.test(source)) {
throw new Error(`${path} contains require('electron') — cannot import from plain-Node/worker entry`)
}
} Prevention
- Before importing a shared module into a daemon, sidecar, or worker entry, trace whether that module (or its transitive imports) calls require('electron').
- Keep electron-aware code in separate files with clear naming (e.g., electron-utils.ts) that plain-Node entries never import.
- Run the build locally after any import change in a guarded entry's graph — the guard fires at build time, not runtime.
- Use lazy require('electron') only inside functions that the plain-Node path never executes, but note the static regex still flags it — restructure to avoid the edge.
When it happens
Trigger: Adding an import to a module in the daemon-entry, parcel-watcher-process-entry, computer-sidecar, agent-hooks, codex grant entry, or any worker thread entry (stt-worker, warp-theme-parser-worker, session-scanner-*, main-thread-hang-watchdog, port-scan-command-worker) that directly or transitively reaches a module containing `require("electron")`. The ELECTRON_REQUIRE_RE regex matches both bare `require("electron")` and subpath forms like `require("electron/main")`.
Common situations: Refactoring a shared utility to import from an electron-aware module. Adding a new import to a daemon or sidecar entry that pulls in a module graph containing `require("electron")`. The port-scan worker is called out as sitting one import away from a client module that deliberately requires electron.
Related errors
- [plain-node-entry-guard] guarded ${missing.map((name) => `"$
- [plain-node-entry-guard] could not smoke-load daemon-entry.j
- [plain-node-entry-guard] daemon-entry.js did not exit within
- [plain-node-entry-guard] daemon-entry.js was killed by ${res
- [plain-node-entry-guard] daemon-entry.js failed to load unde
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/77a754b8bbf6f356.
Report an issue: GitHub.