can1357/oh-my-pi · error

RPC protocol v2 negotiation failed

Error message

RPC protocol v2 negotiation failed

What it means

After sending the negotiate_protocol command, start() validates the response: it must be successful, carry command 'negotiate_protocol', and report data.protocolVersion === 2. Any deviation throws because the client requires v2 to proceed (chunked framing, custom tools).

Source

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

		// Timeout to prevent hanging forever
		const readyTimeout = this.#startTimeout(30000, () => {
			if (readySettled) return;
			readySettled = true;
			readyReject(new Error(`Timeout waiting for agent to become ready. Stderr: ${child.peekStderr()}`));
		});

		try {
			await readyPromise;
			if (protocolV2Supported) {
				protocolV2Enabled = true;
				const response = await this.#send({ type: "negotiate_protocol", protocolVersion: 2 });
				if (
					!response.success ||
					response.command !== "negotiate_protocol" ||
					!isRecord(response.data) ||
					response.data.protocolVersion !== 2
				)
					throw new Error("RPC protocol v2 negotiation failed");
				this.#protocolVersion = 2;
			}
			if (this.#customTools.length > 0) {
				await this.setCustomTools(this.#customTools);
			}
		} catch (cause) {
			// Startup failed after spawning the child. Reap it before returning
			// so a retry cannot inherit a live worker or its session lock.
			const error = cause instanceof Error ? cause : new Error(String(cause));
			await reapAfterOutputFailure(error);
			throw cause;
		} finally {
			clearTimeout(readyTimeout);
		}
	}

	/**
	 * Stop the RPC agent process.

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a child binary that supports RPC protocol v2 (update omp / the spawned CLI).
  2. Inspect the child's stderr/log for why negotiation failed.
  3. Remove any override forcing an old binary version.
  4. If v1 is acceptable, use a client path that does not require v2 negotiation.

Example fix

// before
const client = new RpcClient({ binPath: oldOmpV1 });
// after
const client = new RpcClient({ binPath: updatedOmpWithV2 });
Defensive patterns

Strategy: retry

Validate before calling

const version = await probeChildProtocolVersion(binPath);
if (version < 2) throw new Error("child does not support RPC protocol v2");

Try / catch

try {
  await client.start();
} catch (err) {
  if ((err as Error).message === "RPC protocol v2 negotiation failed") {
    const retry = new RpcClient({ ...opts, binPath: updatedBinPath });
    await retry.start(); // retry with a known-v2 binary
  } else throw err;
}

Prevention

When it happens

Trigger: The child responds success:false to negotiate_protocol, echoes a different command, omits data, or reports a protocolVersion other than 2.

Common situations: Running an older child binary that only speaks protocol v1 or rejects negotiation; spawning a non-omp process; a custom RPC server implementation that mishandles the negotiate command.

Related errors


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