PrefectHQ/fastmcp · error · ValueError
Discovery tool name '{self.execute_tool_name}' collides with
Error message
Discovery tool name '{self.execute_tool_name}' collides with execute_tool_name. What it means
When CodeMode builds its discovery tools it checks that none of them shares the reserved execute_tool_name used for the execute tool. A duplicate means two tools with the same name would be exposed, so a ValueError is raised at transform build time.
Source
Thrown at fastmcp_slim/fastmcp/experimental/transforms/code_mode.py:540
self.max_tool_calls = max_tool_calls
self.sandbox_provider = sandbox_provider or MontySandboxProvider()
self._discovery_factories = (
discovery_tools
if discovery_tools is not None
else _default_discovery_tools()
)
self._built_discovery_tools: list[Tool] | None = None
self._cached_execute_tool: Tool | None = None
def _build_discovery_tools(self) -> list[Tool]:
if self._built_discovery_tools is None:
tools = [
factory(self.get_tool_catalog) for factory in self._discovery_factories
]
names = {t.name for t in tools}
if self.execute_tool_name in names:
raise ValueError(
f"Discovery tool name '{self.execute_tool_name}' "
f"collides with execute_tool_name."
)
if len(names) != len(tools):
raise ValueError("Discovery tools must have unique names.")
self._built_discovery_tools = tools
return self._built_discovery_tools
async def transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
return [*self._build_discovery_tools(), self._get_execute_tool()]
async def get_tool(
self,
name: str,
call_next: GetToolNext,
*,
version: VersionSpec | None = None,
) -> Tool | None:View on GitHub (pinned to 1f02114297)
Solutions
- Rename the discovery tool returned by your custom factory so it differs from execute_tool_name
- Change CodeModeTransform's execute_tool_name to a non-colliding value
- Review your discovery factories' naming scheme to avoid the reserved execute name
Example fix
// before CodeModeTransform(execute_tool_name="catalog") # factory also emits "catalog" // after CodeModeTransform(execute_tool_name="execute_tool") # no collision
Defensive patterns
Strategy: validation
Validate before calling
names = [t.name for f in discovery_factories for t in [f(get_tool_catalog)]]
if execute_tool_name in names:
raise ValueError(f"Discovery tool '{execute_tool_name}' collides with execute_tool_name") Type guard
def discovery_names_valid(names: list[str], execute_tool_name: str) -> bool:
return execute_tool_name not in names Try / catch
try:
tools = await transform.transform_tools(existing_tools)
except ValueError as e:
if "collides with execute_tool_name" in str(e):
# rename your discovery tool or change execute_tool_name
...
raise Prevention
- Never name a custom discovery tool the same as execute_tool_name (default 'execute')
- Pick a distinct namespace prefix for all discovery tool names
- Add a unit test building the transform with your factories before deploy
When it happens
Trigger: Constructing CodeModeTransform with discovery tool factories (or an execute_tool_name) such that a discovery tool's generated name equals execute_tool_name; _build_discovery_tools is then invoked via transform_tools() or get_tool().
Common situations: Custom discovery factory returning a tool named 'execute' (the default execute name); explicitly setting execute_tool_name to a name a discovery factory already uses.
Related errors
- Discovery tools must have unique names.
- CodeMode requires pydantic-monty for the Monty sandbox provi
- Tool call limit exceeded: at most {max_tool_calls} call_tool
- Unknown tool: {tool_name}
- Tool name collision: {public_name!r}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/3655936d3a3cd02d.
Report an issue: GitHub.