stablyai/orca · error

[plain-node-entry-guard] could not smoke-load daemon-entry.j

Error message

[plain-node-entry-guard] could not smoke-load daemon-entry.js under plain Node: ${result.error.message}

What it means

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.

Source

Thrown at config/build-plugins/plain-node-entry-guard.ts:194

    child.on('error', (error: Error) => finish(null, null, error))
    // 'close' gives the full stderr; 'exit' is the fallback so a held-open pipe
    // cannot outlast the process itself.
    child.on('close', (status, signal) => finish(status, signal))
    child.on('exit', (status, signal) => {
      drainTimer = setTimeout(() => finish(status, signal), SMOKE_STDERR_DRAIN_MS)
    })
  })
}

// 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

View on GitHub (pinned to 1136503c6a)

Solutions

  1. 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.
  2. 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.
  3. Ensure the build output directory is writable and the daemon-entry.js file exists at `join(outputDir, 'daemon-entry.js')` before closeBundle runs.
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the daemon entry exists before the build's closeBundle phase
import { existsSync } from 'fs'
import { join } from 'path'
function assertDaemonEntryExists(outputDir) {
  const entryPath = join(outputDir, 'daemon-entry.js')
  if (!existsSync(entryPath)) {
    throw new Error(`daemon-entry.js not found at ${entryPath} — check rollup output config`)
  }
}

Try / catch

// The spawn error is environmental; wrap in a diagnostic message
try {
  await smokeLoadDaemonEntry(outputDir, timings)
} catch (error) {
  if (error.message.includes('could not smoke-load')) {
    console.error('Spawn failed — check Node binary and file permissions:', error.message)
    console.error('Entry path:', join(outputDir, 'daemon-entry.js'))
  }
  throw error
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/9ebdd8d2bfe60739. Report an issue: GitHub.