Skip to content

tools: Tool names must be unique

The tool array your client sends with the request contains the same name twice, and validation rejected the whole request before any model saw it. Nothing ran, nothing was billed for output, and no model made a mistake. This is a property of your configuration, not of your conversation, which means /clear, /compact and /rewind are all guaranteed to change nothing, and a brand-new session fails on its first message just as reliably as the one you were working in. It is not a context limit, not a quota, and not an outage.

The explanation everyone reaches for is usually wrong

The obvious reading is that two MCP servers both expose a tool called search, and you now have to rename one of them. That reading is wrong most of the time, and believing it sends you editing servers that are not involved.

Tools contributed by an MCP server are namespaced with the server’s name before they reach the array. That is the entire point of the prefix: two servers can both expose search and the assembled list still contains two distinct entries. It is also, incidentally, why the name-length error measures a longer string than the one you wrote — the prefix is real, it is in the name, and it is doing collision avoidance for you.

So if prefixing normally prevents this, a duplicate means prefixing did not happen or did not disambiguate. There are only a few ways to arrive there:

  • A gateway or proxy server flattened several servers into one namespace. This is the case where the naive explanation becomes correct — but only because something merged the namespaces first. An aggregator that fans out to several upstream servers and re-publishes their tools appears to your client as one server with one prefix, so two upstream search tools land in the array as two identical names. If you added a gateway for convenience, start here.
  • A single server emits duplicate names in its own tool list. Servers that generate tools from an API description produce this whenever the source document has two operations with the same identifier. Your configuration looks perfect, because the problem is inside one entry of it.
  • A non-MCP contributor collides with another. Plugins, skills, and built-in tools all put names in the same array. A plugin that ships a tool named like an existing one produces exactly this error with no MCP server involved at all.

Note what is not on that list: registering the same server twice under two different names. That produces two prefixes and two valid sets of entries — no error, just twice the tool definitions sitting in your context window on every request, which is a budget problem rather than a validation one.

There is no index, and that is the practical difficulty

Its sibling error names a position, tools.48, and gives you something to count to. This message names nothing. It tells you a set has a repeat in it and leaves you to find which.

That makes the assembled list the only artifact worth looking at, and it is not a file you already have. Your configuration files show the names before prefixing and before plugins and built-ins are merged in; the array that failed validation exists only at request time. Enable debug logging and read the assembled tool list from ~/.claude/debug/<session-id>.txt (%USERPROFILE%\.claude\debug\ on Windows), then sort the names and look for the pair. This takes a minute and replaces an unbounded amount of guessing.

One caution before you paste any of that into an issue: debug output and diagnostics can carry server names, URLs and tokens. The /heapdump diagnostics are explicit about this — the heap snapshot contains every string in the process, including the conversation and credentials, and the docs say not to attach it to a public issue. Share the tool-name list, not the raw log, and mask anything credential-shaped.

Is retrying useful?

No, and this is a retry with a cost.

The array is rebuilt identically on every request until you change a configuration file, so every attempt reproduces the same duplicate. Worse, the failure happens at validation, which is fast and cheap for the provider and returns immediately to you — so an automated retry wrapper turns one misconfiguration into a rapid burst of rejected requests, which is how a deterministic error starts looking like a rate-limit problem.

The one thing worth doing more than once is not a retry: after you remove one duplicate, the next request can fail on a different pair. That is not your fix failing. It means there were two collisions and you have found the first. Check the whole sorted list in one pass rather than fixing them one error at a time.

Narrowing it down, in the order that costs least

  • Does a fresh session fail on a one-word prompt? If yes, the duplicate is in your base tool list and every request is affected. If a plain prompt works and the error appears only when a subagent or a dispatched task starts, then the duplicate is created by the list assembled for that path, not by your base configuration — and the servers and plugins you should be inspecting are the ones that path adds. This distinction saves the most time of anything on this page, because a healthy-looking main session convinces people the config is fine.
  • Start with everything off. claude --safe-mode starts with all plugins, MCP servers and hooks disabled. If the error vanishes, it comes from one of those rather than from a built-in tool.
  • Bring them back one at a time. The first one that reintroduces the error is either the duplicate’s source or its partner. If enabling a single server reproduces it with nothing else on, that server is emitting duplicates by itself, and no amount of renaming on your side will fix it.
  • Check your scopes. MCP servers can be defined per-project in .mcp.json, per-user, or locally, and a server you forgot about in one scope is still contributing tools in every session. /mcp shows what is actually connected, which is more reliable than the file you happen to have open.

Fixing the pair you found

  • A gateway flattened namespaces — configure its exposure list so it publishes only the upstream tools you need, or connect the upstream servers directly and drop the gateway. Direct connections get their own prefixes back.
  • One server emits duplicates — that is a bug in the server or in the description it generates from. If it supports filtering which tools it exposes, exclude one of the pair; otherwise pin to a version that worked and report it upstream.
  • A plugin collides with a built-in or another plugin — disable the plugin and decide whether you need the tool it adds more than the one it shadows.
  • The collision only appears on the subagent path — reduce the tool set that path is given. Subagents rarely need every server the main session has, and a smaller list is faster and cheaper as well as valid.
  • You assemble tools yourself against the API — check uniqueness where you build the array and fail loudly in your own code. The provider will reject a duplicate every time; catching it in a test costs nothing, and catching it in production costs every request until someone reads this message.

Confirming it is gone

Re-enable everything you disabled during isolation before you test — a success with half your servers off proves only that half your servers are not the problem.

Then do two things, not one. Start a new session and send a trivial prompt, and separately re-run the exact path that failed, including a subagent dispatch if that was where you saw it. Both have to pass, because they build different arrays. Finally, look at the sorted tool list in the debug log once more and confirm there are no remaining repeats, rather than concluding from a single success that the last one is gone.

If tool trouble starts mid-conversation instead of on the first request, the cause is stored history rather than configuration — a message that names tool use concurrency and tells you to run /rewind is repaired by rewinding, not editing. If several tool calls in one batch fail together and only one of them says anything specific, read why a sibling tool call error is not the error you need before you debug the wrong call. And if the tools from a server are missing entirely rather than duplicated, the server probably never connected, which fails silently in the opposite direction. The AI coding error triage tool sorts configuration-time rejections like this one from failures that depend on your conversation, which is the first question this page answers.