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

  1. Rename the RPC tool to a non-colliding name (e.g. prefix with your host: 'myhost_run_cmd').
  2. List the existing tool names first (session.getEnabledToolNames / registry) and choose unique names.
  3. 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

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


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