PrefectHQ/fastmcp · error
ToolTransform has duplicate target name
Error message
ToolTransform has duplicate target name {target!r}: both {seen_targets[target]!r} and {original_name!r} map to it What it means
ToolTransform validates that every transformed tool maps to a unique public target name. Each entry's target name is its explicit `name` if set, otherwise the original tool name. If two entries resolve to the same target, the transform map would be ambiguous at lookup time, so the constructor raises ValueError immediately.
Solutions
- Pick a distinct `name` for one of the colliding transform entries so targets are unique.
- Remove one of the duplicate entries if it is redundant (e.g. left over from an old config).
- Prefix or namespace generated target names (e.g. `api_<tool>`) in scripted configs to avoid accidental collisions.
Example fix
# before
transforms = ToolTransform({
"list_items": ToolTransformConfig(name="items"),
"get_items": ToolTransformConfig(name="items"), # duplicate target 'items'
})
# after
transforms = ToolTransform({
"list_items": ToolTransformConfig(name="list_items_view"),
"get_items": ToolTransformConfig(name="get_items_view"),
}) Defensive patterns
Strategy: validation
Validate before calling
targets = [(k, c.name or k) for k, c in transforms.items()]
dups = [t for t in set(x[1] for x in targets) if [x[1] for x in targets].count(t) > 1]
if dups:
raise ValueError(f"Duplicate transform target names: {dups}") Try / catch
try:
t = ToolTransform(transforms)
except ValueError as e:
if "duplicate target name" in str(e):
log.error(f"Transform config conflict: {e}")
raise SystemExit(2) Prevention
- Centralize tool renaming in one config and assert uniqueness in tests.
- Namespace scripted target names to avoid accidental collisions.
- Lint transform dicts for duplicate resolved targets in CI.
When it happens
Trigger: Calling `ToolTransform({...})` (or a subclass like the renaming transform) with a dict where two entries produce the same target: e.g. two entries renaming different originals to the same new name, or an explicit `name` on one entry colliding with another entry's original tool name.
Common situations: Bulk-renaming configs generated by hand or script where a rename accidentally matches an existing tool name; copying a transform config between environments where one tool was renamed upstream; adding a new renamed entry without noticing the target name already exists.
Related errors
- At least one of version_gte or version_lt must be specified
- cache_ttl must be a positive integer, got
- Cannot determine tool name for
- Cannot resolve tool reference
- Cannot specify both 'default' and 'default_factory' in…
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/90a208ec64da05cc.
Report an issue: GitHub.
Appendix: source
Thrown at fastmcp_slim/fastmcp/server/transforms/tool_transform.py:52
"""Initialize ToolTransform.
Args:
transforms: Map of original tool name → transform config.
"""
self._transforms = transforms
# Build reverse mapping: final_name → original_name
self._name_reverse: dict[str, str] = {}
for original_name, config in transforms.items():
final_name = config.name if config.name else original_name
self._name_reverse[final_name] = original_name
# Validate no duplicate target names
seen_targets: dict[str, str] = {}
for original_name, config in transforms.items():
target = config.name if config.name else original_name
if target in seen_targets:
raise ValueError(
f"ToolTransform has duplicate target name {target!r}: "
f"both {seen_targets[target]!r} and {original_name!r} map to it"
)
seen_targets[target] = original_name
def __repr__(self) -> str:
names = list(self._transforms.keys())
if len(names) <= 3:
return f"ToolTransform({names!r})"
return f"ToolTransform({names[:3]!r}... +{len(names) - 3} more)"
async def list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool]:
"""Apply transforms to matching tools."""
result: list[Tool] = []
for tool in tools:
if tool.name in self._transforms:
transformed = self._transforms[tool.name].apply(tool)
result.append(transformed)View on GitHub (pinned to 1f02114297)