n8n-io/n8n · error · Error

Failed to spawn daemon: `${command}` did not start. See ${ar

Error message

Failed to spawn daemon: `${command}` did not start. See ${args.logPath} for details.

What it means

Thrown by `spawnDaemonDetached()` when `spawn(...)` returns a child whose `pid` is `undefined`. Node sets `pid` to undefined when the child could not be spawned at all (most commonly ENOENT — the command doesn't exist on PATH). The function also attaches a one-shot `'error'` listener that writes the underlying spawn error to the daemon log and the eval logger, so the message deliberately points at the log for the real OS-level cause rather than guessing.

Source

Thrown at packages/@n8n/instance-ai/evaluations/computer-use/daemon.ts:221

			// the UI clicks Connect itself between scenarios. Avoids the manual
			// click each time `browser_disconnect` resets the session at the end
			// of a credential-setup orchestration run.
			env: { ...process.env, FORCE_COLOR: '0', N8N_EVAL_AUTO_BROWSER_CONNECT: '1' },
		});

		// `spawn` reports failures asynchronously via 'error' (e.g. ENOENT when the
		// command isn't on PATH). With a detached/unref'd child, an unhandled
		// 'error' event would crash the parent. Surface the failure in both the
		// daemon log and the eval logger so the pairing-poll timeout that follows
		// has a real cause attached, rather than just timing out silently.
		child.once('error', (error: Error) => {
			const message = `[daemon] spawn failed (${command}): ${error.message}\n`;
			args.logger.error(`Failed to spawn daemon (${command}): ${error.message}`);
			void appendFile(args.logPath, message).catch(() => {});
		});

		if (child.pid === undefined) {
			throw new Error(
				`Failed to spawn daemon: \`${command}\` did not start. See ${args.logPath} for details.`,
			);
		}
		child.unref();
		return child.pid;
	} finally {
		await logFile.close();
	}
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Open `.eval-output/daemon.log` — the `[daemon] spawn failed (<command>): <error>` line names the exact OS error (usually `spawn npx ENOENT`).
  2. If ENOENT for `npx`: ensure Node/npm is installed and on PATH for the shell launching the eval; for a published-daemon run you can also switch to `--use-published-daemon=false` (the default) which uses `process.execPath` directly and doesn't need npx on PATH.
  3. If ENOENT for the local build: that's a different error (424); confirm the build exists.
  4. If the log shows no spawn-failed line at all, the failure happened before the listener attached — check that the log fd was opened successfully and that `process.execPath` is valid.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  pid = await spawnDaemonDetached(args);
} catch (e) {
  if (e instanceof Error && /did not start/.test(e.message)) {
    const log = await readFile(args.logPath, 'utf-8').catch(() => '(no log)');
    throw new Error(`${e.message}\n--- daemon.log ---\n${log}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: `usePublishedDaemon` is true but `npx` is not on PATH (no Node, misconfigured PATH, nvm not loaded in the spawning shell). `usePublishedDaemon` is false but `process.execPath` somehow resolves to a non-existent binary (extremely rare; would indicate a corrupted Node install). On some systems, spawning a detached child with `stdio` piped to a just-opened log fd can fail synchronously if the fd is invalid.

Common situations: Running under a process supervisor (systemd, pm2) that doesn't inherit the developer's PATH, so `npx` isn't found. Containerized runs where Node is installed but `npx` isn't symlinked. A typo'd or overridden NODE_OPTIONS / NODE_PATH that breaks spawn. The spawn 'error' event fired with ENOENT and was logged, then the synchronous `child.pid === undefined` check ran immediately after.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/2a6bfded4f39dae0. Report an issue: GitHub.