can1357/oh-my-pi · error

Client not started

Error message

Client not started

What it means

#send() throws synchronously if the client has no spawned process/stdin — i.e. start() was never called, or stop()/process exit already cleared this.#process. No RPC command can be written without an established child process.

Source

Thrown at packages/coding-agent/src/modes/rpc/rpc-client.ts:1128

			return;
		}

		if (!isAgentSessionEvent(data)) return;

		for (const listener of this.#sessionEventListeners) {
			listener(data);
		}

		if (!isAgentEvent(data)) return;

		for (const listener of this.#eventListeners) {
			listener(data);
		}
	}

	#send(command: RpcCommandBody, timeoutMs = 30_000): Promise<RpcResponse> {
		if (!this.#process?.stdin) {
			throw new Error("Client not started");
		}

		const id = `req_${++this.#requestId}`;
		const fullCommand = { ...command, id } as RpcCommand;
		const { promise, resolve, reject } = Promise.withResolvers<RpcResponse>();
		let settled = false;
		const timeoutId = this.#startTimeout(timeoutMs, () => {
			if (settled) return;
			this.#pendingRequests.delete(id);
			settled = true;
			reject(
				new Error(`Timeout waiting for response to ${command.type}. Stderr: ${this.#process?.peekStderr() ?? ""}`),
			);
		});

		this.#pendingRequests.set(id, {
			resolve: response => {
				if (settled) return;

View on GitHub (pinned to 9690622007)

Solutions

  1. await client.start() before issuing any command.
  2. Check the client's running state before sending, especially after stop().
  3. Re-create and start a client after the previous one was stopped or crashed.
  4. Serialize your lifecycle: gate commands on a ready-promise so they wait for start to finish.

Example fix

// before
const client = new RpcClient(opts);
const msgs = await client.getMessages(); // throws
// after
const client = new RpcClient(opts);
await client.start();
const msgs = await client.getMessages();
Defensive patterns

Strategy: try-catch

Validate before calling

if (!client.isRunning()) await client.start();

Try / catch

try {
  const res = await client.sendCommand(cmd);
} catch (err) {
  if ((err as Error).message === "Client not started") {
    await client.start();
  } else throw err;
}

Prevention

When it happens

Trigger: Calling any command that routes through #send before start(), or after stop() or an unexpected child exit cleared this.#process.

Common situations: Forgetting to await client.start() during setup; issuing commands after the session was stopped; a crash of the child process leaving the client without a process; async races where a queued command fires post-stop.

Related errors


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