spring-projects/spring-ai · error · IllegalStateException
Multiple tools with the same name (%s) found in ToolCallingC
Error message
Multiple tools with the same name (%s) found in ToolCallingChatOptions
What it means
ToolCallingChatOptions.validateToolCallbacks rejects a tool callback list containing two or more callbacks with the same tool name, since the model and manager could not disambiguate which implementation to invoke. It uses ToolUtils.getDuplicateToolNames and throws IllegalStateException listing the duplicated names.
Source
Thrown at spring-ai-model/src/main/java/org/springframework/ai/model/tool/ToolCallingChatOptions.java:98
return defaultToolContext != null ? Map.copyOf(defaultToolContext) : null;
}
Assert.noNullElements(runtimeToolContext.keySet(), "runtimeToolContext keys cannot be null");
if (CollectionUtils.isEmpty(defaultToolContext)) {
return Map.copyOf(runtimeToolContext);
}
Assert.noNullElements(defaultToolContext.keySet(), "defaultToolContext keys cannot be null");
var mergedToolContext = new java.util.HashMap<>(defaultToolContext);
mergedToolContext.putAll(runtimeToolContext);
return Map.copyOf(mergedToolContext);
}
static void validateToolCallbacks(@Nullable List<ToolCallback> toolCallbacks) {
if (CollectionUtils.isEmpty(toolCallbacks)) {
return;
}
List<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);
if (!duplicateToolNames.isEmpty()) {
throw new IllegalStateException("Multiple tools with the same name (%s) found in ToolCallingChatOptions"
.formatted(String.join(", ", duplicateToolNames)));
}
}
/**
* A builder to create a {@link ToolCallingChatOptions} instance.
*/
interface Builder<B extends Builder<B>> extends ChatOptions.Builder<B> {
/**
* ToolCallbacks to be registered with the ChatModel.
*/
B toolCallbacks(@Nullable List<ToolCallback> toolCallbacks);
/**
* ToolCallbacks to be registered with the ChatModel.
*/
B toolCallbacks(ToolCallback... toolCallbacks);View on GitHub (pinned to 98a7beda4f)
Solutions
- Deduplicate the callback list before setting it (filter by ToolCallback.getName())
- Rename one of the conflicting tools (unique @Tool name / FunctionCallback name)
- Inspect which providers contributed duplicates (log each callback name before building options)
- If both tools are wanted, wrap or prefix names so each is unique
Example fix
// before options.setToolCallbacks(List.of(providerA.getToolCallbacks(), providerB.getToolCallbacks()).stream().flatMap(List::stream).toList()); // after Set<String> seen = new HashSet<>(); List<ToolCallback> unique = all.stream().filter(cb -> seen.add(cb.getName())).toList(); options.setToolCallbacks(unique);
Defensive patterns
Strategy: validation
Validate before calling
List<ToolCallback> cbs = options.getToolCallbacks();
Set<String> names = new HashSet<>();
for (ToolCallback cb : cbs) {
if (!names.add(cb.getName())) {
throw new IllegalStateException("Duplicate tool name: " + cb.getName());
}
} Prevention
- Deduplicate callbacks whenever merging multiple ToolCallbackProviders
- Namespace tool names per provider (e.g. mcp_search vs local_search)
- Unit-test the assembled options for duplicate names before hitting the model
When it happens
Trigger: Building ToolCallingChatOptions (or calling a ChatModel with them) where getToolCallbacks() contains callbacks with identical names, e.g. after merging tool lists from multiple ToolCallbackProviders or registering the same @Tool method twice.
Common situations: Combining tool sources (MCP client + local tools) that both expose a tool named e.g. 'search', accidentally registering the same FunctionCallback twice, or version migrations registering defaults plus explicit duplicates.
Related errors
- At most one ToolAdvisor is allowed in the advisor chain, but
- Only outputType or outputJsonSchema can be set, not both.
- Tool call limit exceeded (dynamic message: tool name and lim
- No ToolCallback found for tool name: %s
- argumentType is required
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/56779169cab51cd9.
Report an issue: GitHub.