can1357/oh-my-pi · error
RPC host tool "${name}" conflicts with an existing tool
Error message
RPC host tool "${name}" conflicts with an existing tool What it means
When refreshing RPC host tools, SessionTools checks each incoming tool name against the existing tool registry. If a name already exists but was NOT previously owned by the RPC host (i.e. it's a built-in or other-source tool), the refresh throws instead of silently overwriting it. This protects built-in tools from being shadowed by externally supplied RPC tools.
Source
Thrown at packages/coding-agent/src/session/session-tools.ts:1830
}
}
/** Replaces RPC host-owned tools and refreshes the active set before the next model call. */
refreshRpcHostTools(rpcTools: AgentTool[]): Promise<void> {
const snapshot = [...rpcTools];
return this.runToolRegistryMutation(() => this.#applyRpcHostToolRefresh(snapshot));
}
async #applyRpcHostToolRefresh(rpcTools: AgentTool[]): Promise<void> {
const nextToolNames = rpcTools.map(tool => tool.name);
const uniqueToolNames = new Set(nextToolNames);
if (uniqueToolNames.size !== nextToolNames.length) {
throw new Error("RPC host tool names must be unique");
}
for (const name of uniqueToolNames) {
if (this.#toolRegistry.has(name) && !this.#rpcHostToolNames.has(name)) {
throw new Error(`RPC host tool "${name}" conflicts with an existing tool`);
}
}
const previousRpcHostToolNames = new Set(this.#rpcHostToolNames);
const previousActiveToolNames = this.getEnabledToolNames();
const previousRpcHostTools = new Map(
[...previousRpcHostToolNames].flatMap(name => {
const tool = this.#toolRegistry.get(name);
return tool ? [[name, tool] as const] : [];
}),
);
for (const name of previousRpcHostToolNames) {
this.#toolRegistry.delete(name);
}
this.#rpcHostToolNames.clear();
const extensionRunner = this.#host.extensionRunner();
for (const tool of rpcTools) {View on GitHub (pinned to 9690622007)
Solutions
- Rename the RPC tool to a non-colliding name (e.g. prefix with your host: 'myhost_run_cmd').
- List the existing tool names first (session.getEnabledToolNames / registry) and choose unique names.
- If intentional replacement is needed, do it through the sanctioned tool-override mechanism rather than the RPC tool refresh path.
Example fix
// before: collides with built-in
await session.setRpcTools([{ name: 'bash', ... }]);
// after
await session.setRpcTools([{ name: 'remote_bash', ... }]); Defensive patterns
Strategy: validation
Validate before calling
const existing = new Set(session.getEnabledToolNames());
const clashes = tools.filter(t => existing.has(t.name)).map(t => t.name);
if (clashes.length) throw new Error(`names collide with built-ins: ${clashes}`); Try / catch
try {
await session.setRpcTools(tools);
} catch (err) {
if (/conflicts with an existing tool/.test(err.message)) {
logger.error('rename RPC tools; collision with built-in', { err });
} else throw err;
} Prevention
- Prefix RPC tool names with a host-specific namespace (e.g. 'myhost_').
- Compare against the current registry (getEnabledToolNames) before registering.
- Never name RPC tools after built-ins like bash/read/edit.
- Keep a fixed manifest of RPC tool names and lint it against the agent's built-in list.
When it happens
Trigger: An RPC tool refresh supplies a tool whose name collides with an existing registry tool not registered by the RPC host (session-tools.ts:1828-1832): `#toolRegistry.has(name) && !#rpcHostToolNames.has(name)`.
Common situations: RPC host registering a tool named 'bash', 'read', 'edit' or another built-in name; renaming a local tool to collide with an RPC tool; loading tool packs that use generic names.
Related errors
- Host tool at index ${index} must provide a non-empty name
- Host tool "${name}" must provide a non-empty description
- Host tool "${name}" must provide a JSON Schema object
- RPC host tool names must be unique
- Unsupported language '{value}'. Supported: {}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f64293a35e5cfd76.
Report an issue: GitHub.