spring-projects/spring-ai · error · IllegalStateException
Multiple tools with the same name
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.
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.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- argumentType is required
- At most one ToolAdvisor is allowed in the advisor chain…
- Either vectorStore or jedisClient must be provided
- EmbeddingModel must be provided
- Failed to obtain the embedding dimensions from the…
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/56779169cab51cd9.
Report an issue: GitHub.
Appendix: 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)