can1357/oh-my-pi · critical

Failed to start daemon broker: ${lastError?.message ?? "sock

Error message

Failed to start daemon broker: ${lastError?.message ?? "socket unavailable"}

What it means

#connectOnce attempts to connect to the broker socket and, if unreachable, spawns the broker and retries within a deadline. When all attempts fail, it throws this aggregate error carrying the last underlying failure's message (or 'socket unavailable' if none was captured).

Source

Thrown at packages/coding-agent/src/launch/client.ts:305

			this.#bindSocket(await openSocket(this.#endpoint, 250));
			return;
		} catch {
			// No live broker. Multiple clients may race to spawn; the broker's PID
			// lease selects one winner before any candidate touches the socket.
		}
		this.#spawnBroker();
		const deadline = Date.now() + CONNECT_TIMEOUT_MS;
		let lastError: Error | undefined;
		while (Date.now() < deadline) {
			try {
				this.#bindSocket(await openSocket(this.#endpoint, 250));
				return;
			} catch (error) {
				lastError = error instanceof Error ? error : new Error(String(error));
				await Bun.sleep(CONNECT_RETRY_MS);
			}
		}
		throw new Error(`Failed to start daemon broker: ${lastError?.message ?? "socket unavailable"}`);
	}

	#spawnBroker(): void {
		const spawn = resolveWorkerSpawnCmd(DAEMON_BROKER_WORKER_ARG);
		const overlay: Record<string, string> = {
			[DAEMON_PROJECT_DIR_ENV]: this.projectDir,
			[DAEMON_RUNTIME_DIR_ENV]: this.#runtimeDir,
		};
		if (this.#idleGraceMs !== undefined) overlay[DAEMON_IDLE_GRACE_ENV] = String(this.#idleGraceMs);
		const child = Bun.spawn(spawn.cmd, {
			cwd: spawn.cwd,
			env: workerEnvFromParent(overlay),
			stdin: "ignore",
			stdout: "ignore",
			stderr: "ignore",
			...BROKER_SPAWN_OPTIONS,
		});
		child.unref();

View on GitHub (pinned to 9690622007)

Solutions

  1. Read lastError in the message — fix the underlying broker startup failure first
  2. Verify the runtime dir is writable and the worker entry resolves (workerHostEntry / fallback)
  3. Run the broker in the foreground to see its real startup error, then retry the client

Example fix

// before
await client.request(op); // throws aggregate start failure
// after
try { await client.request(op); }
catch (err) {
  logger.error("daemon broker start failed", { cause: err.message }); // inspect lastError, fix root cause
  throw err;
}
Defensive patterns

Strategy: retry

Validate before calling

const socketPath = daemonBrokerEndpoint(projectDir, runtimeDir);
await fs.mkdir(runtimeDir, { recursive: true });
await fs.access(runtimeDir, fs.constants.W_OK); // runtime dir must be writable before connecting

Try / catch

try {
  await client.request({ op: 'ping' });
} catch (err) {
  if (err.message.startsWith('Failed to start daemon broker')) {
    logger.error('broker start failed', { cause: err.message }); // message embeds lastError — fix root cause
  }
  throw err;
}

Prevention

When it happens

Trigger: Broker repeatedly fails to start (bad env, missing worker entry, crash loop) or the socket path is unusable, exhausting the connect deadline.

Common situations: Runtime dir on a filesystem without unix socket support; broker worker crashing at startup (see env/token errors); permission issues on the socket file; port/path collisions between projects.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/fec571f43f55cc91. Report an issue: GitHub.