{"record":{"id":"bf3157a291bd13ba","repo":"can1357/oh-my-pi","slug":"rpc-host-tool-names-must-be-unique","errorCode":null,"errorMessage":"RPC host tool names must be unique","messagePattern":"RPC host tool names must be unique","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/session-tools.ts","lineNumber":1825,"sourceCode":"\t\t\tawait this.#applyActiveToolsByName(nextActive);\n\t\t\tif (this.#host.isDisposed()) restorePreviousMcpTools();\n\t\t} catch (error) {\n\t\t\trestorePreviousMcpTools();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/** Replaces RPC host-owned tools and refreshes the active set before the next model call. */\n\trefreshRpcHostTools(rpcTools: AgentTool[]): Promise<void> {\n\t\tconst snapshot = [...rpcTools];\n\t\treturn this.runToolRegistryMutation(() => this.#applyRpcHostToolRefresh(snapshot));\n\t}\n\n\tasync #applyRpcHostToolRefresh(rpcTools: AgentTool[]): Promise<void> {\n\t\tconst nextToolNames = rpcTools.map(tool => tool.name);\n\t\tconst uniqueToolNames = new Set(nextToolNames);\n\t\tif (uniqueToolNames.size !== nextToolNames.length) {\n\t\t\tthrow new Error(\"RPC host tool names must be unique\");\n\t\t}\n\n\t\tfor (const name of uniqueToolNames) {\n\t\t\tif (this.#toolRegistry.has(name) && !this.#rpcHostToolNames.has(name)) {\n\t\t\t\tthrow new Error(`RPC host tool \"${name}\" conflicts with an existing tool`);\n\t\t\t}\n\t\t}\n\n\t\tconst previousRpcHostToolNames = new Set(this.#rpcHostToolNames);\n\t\tconst previousActiveToolNames = this.getEnabledToolNames();\n\t\tconst previousRpcHostTools = new Map(\n\t\t\t[...previousRpcHostToolNames].flatMap(name => {\n\t\t\t\tconst tool = this.#toolRegistry.get(name);\n\t\t\t\treturn tool ? [[name, tool] as const] : [];\n\t\t\t}),\n\t\t);\n\t\tfor (const name of previousRpcHostToolNames) {\n\t\t\tthis.#toolRegistry.delete(name);","sourceCodeStart":1807,"sourceCodeEnd":1843,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/session-tools.ts#L1807-L1843","documentation":"#applyRpcHostToolRefresh validates an incoming batch of RPC-provided tools before merging them into the registry. If two or more tools in the same batch share a name, the refresh cannot build an unambiguous registry and throws. The registry requires globally unique tool names because the model dispatches tools by name.","triggerScenarios":"Calling the RPC host tool refresh (setRpcTools / equivalent) with an AgentTool[] where `rpcTools.map(t => t.name)` contains duplicates (session-tools.ts:1824-1827).","commonSituations":"See trigger scenarios.","solutions":["Deduplicate the tool list before sending it: filter by name so each name appears once.","If you need two variants of a tool, give them distinct names (e.g. 'search_files' vs 'search_files_deep').","Fix the RPC host side to send a fresh, unique array rather than appending to a previous list."],"exampleFix":"// before\nawait session.setRpcTools([...tools, ...tools]);\n// after\nconst unique = [...new Map(tools.map(t => [t.name, t])).values()];\nawait session.setRpcTools(unique);","handlingStrategy":"validation","validationCode":"const names = tools.map(t => t.name);\nif (new Set(names).size !== names.length) {\n  throw new Error('duplicate RPC tool names: ' + names.filter((n, i) => names.indexOf(n) !== i));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await session.setRpcTools(tools);\n} catch (err) {\n  if (err.message === 'RPC host tool names must be unique') {\n    const deduped = [...new Map(tools.map(t => [t.name, t])).values()];\n    await session.setRpcTools(deduped);\n  } else throw err;\n}","preventionTips":["Deduplicate tool arrays by name before every refresh call.","Generate tool names from a single source of truth.","Add an assertion in your RPC host that outgoing tool names are unique."],"tags":["rpc","tools","validation","duplicate"],"backgroundTag":"duplicate-tool-name","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}