zylon-ai/private-gpt · error · ToolNameConflictError
TOOL_NAME_CONFLICT
TOOL_NAME_CONFLICT
Error message
Tool name conflict: name='{tool.name}', layer_a='{existing}', layer_b='{layer.source}' What it means
Thrown by ContextStackBuilder while validating global tool-name uniqueness. It walks every layer whose type is LayerType.TOOL_DEFINITIONS (an instance of ToolDefinitionsLayer) and records the first layer.source for each non-None tool.name; a second layer defining the same name raises ToolNameConflictError with both layer sources. Tools with name=None are skipped, and only the first owner is reported per name.
Source
Thrown at private_gpt/components/context/services/context_stack_builder.py:98
def build(self) -> ContextStack:
"""Build an immutable stack after conflict checks."""
self.validate_tool_name_uniqueness()
return ContextStack(layers=list(self.layers))
def validate_tool_name_uniqueness(self) -> None:
"""Validate global tool uniqueness across tool-definition layers."""
seen: dict[str, str] = {}
for layer in self.layers:
if layer.type is not LayerType.TOOL_DEFINITIONS:
continue
if not isinstance(layer, ToolDefinitionsLayer):
continue
for tool in layer.tools:
if tool.name is None:
continue
existing = seen.get(tool.name)
if existing is not None:
raise ToolNameConflictError(
"Tool name conflict: "
f"name='{tool.name}', "
f"layer_a='{existing}', layer_b='{layer.source}'"
)
seen[tool.name] = layer.source
View on GitHub (pinned to 4a030776a3)
Solutions
- Find the two layers named in the message (layer_a / layer_b) and remove or rename the duplicated tool in one of them
- If both copies are the same tool set, drop the redundant ToolDefinitionsLayer instead of passing it twice
- Rename one MCP server's tool via its own tool-name prefix/alias configuration
- Rebuild the stack incrementally (add one tool layer at a time) to identify which layer introduces the conflict
Example fix
# before
stack = builder.build([
tool_layer_from_server_a, # defines 'search'
tool_layer_from_server_b, # also defines 'search' -> conflict
])
# after
stack = builder.build([
tool_layer_from_server_a,
rename_tools(tool_layer_from_server_b, prefix='b_'),
]) Defensive patterns
Strategy: validation
Validate before calling
def find_tool_conflicts(layers) -> dict[str, tuple[str, str]]:
seen, conflicts = {}, {}
for layer in layers:
if getattr(layer, 'type', None).__class__ and layer.type is not None and str(layer.type).endswith('TOOL_DEFINITIONS'):
for tool in getattr(layer, 'tools', []):
if tool.name and tool.name in seen and seen[tool.name] != layer.source:
conflicts[tool.name] = (seen[tool.name], layer.source)
elif tool.name:
seen[tool.name] = layer.source
return conflicts
conflicts = find_tool_conflicts(builder.layers)
assert not conflicts, conflicts Try / catch
try:
stack = builder.build(layers)
except ToolNameConflictError as e:
# message names both conflicting layer sources; drop/rename and rebuild
raise HTTPException(400, str(e)) Prevention
- Namespace/prefix tool names per MCP server or per layer so cross-layer collisions cannot happen
- Deduplicate tool-definition layers before building the stack (same source loaded once)
- Run uniqueness validation in CI over all configured tool sources
When it happens
Trigger: Building a context stack that contains two ToolDefinitionsLayers whose tool lists both contain a tool with the same name (e.g. MCP servers exposing identically named tools, or the same tool set attached at two context levels). Triggered during stack construction/validation, not at tool invocation time.
Common situations: Connecting two MCP servers that both expose 'search' or 'read_file'; loading the same tool catalog twice (global layer + request-level layer); merging contexts from different providers that use generic tool names; upgrading a dependency that renames a built-in tool to collide with your custom tool.
Related errors
- Tool context is provided, but no tools are specified. Please
- MCP servers are not supported when structured output is enab
- LLM mode '{mode}' is not supported. Available: {available}
- No model specified and no models are configured
- Model '{target_model}' not found. Available: {available}
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/ab644acd3b1f2b37.
Report an issue: GitHub.