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
- Use a child binary that supports RPC protocol v2 (update omp / the spawned CLI).
- Inspect the child's stderr/log for why negotiation failed.
- Remove any override forcing an old binary version.
- 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
- Update omp so both sides speak protocol v2.
- Probe child capabilities before start if you support multiple versions.
- Don't point the client at foreign RPC servers.
- Watch release notes for protocol bumps.
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
- invalid glob `{pattern}`: {error}
- RPC chunk received before protocol negotiation
- Replacement text is not valid UTF-8: {err}
- RPC chunk exceeded the transport limit
- invalid rpc chunk data
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e76eb8b02a185e5c.
Report an issue: GitHub.