{"record":{"id":"08e1662419e7d20b","repo":"alibaba/spring-ai-alibaba","slug":"multiple-tools-with-the-same-name-s","errorCode":null,"errorMessage":"Multiple tools with the same name (%s)","messagePattern":"Multiple tools with the same name \\((.+?)\\)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":500,"severity":"warning","filePath":"spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/agent/tool/CompositeToolCallbackProvider.java","lineNumber":131,"sourceCode":"\t\t\t\t\tAppComponentTypeEnum.Workflow);\n\t\t\taddToolCallbacks(toolCallbacks, workflowComponentCallbacks);\n\t\t}\n\n\t\treturn toolCallbacks.toArray(new ToolCallback[0]);\n\t}\n\n\t/**\n\t * Validates that there are no duplicate tool names in the provided callbacks.\n\t * <p>\n\t * This method ensures that each tool has a unique name, which is required for proper\n\t * tool resolution and execution.\n\t * @param toolCallbacks the tool callbacks to validate\n\t * @throws IllegalStateException if duplicate tool names are found\n\t */\n\tprivate void validateToolCallbacks(ToolCallback[] toolCallbacks) {\n\t\tList<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);\n\t\tif (!duplicateToolNames.isEmpty()) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Multiple tools with the same name (%s)\".formatted(String.join(\", \", duplicateToolNames)));\n\t\t}\n\t}\n\n\t/**\n\t * Creates a list of tool callbacks based on the provided configuration and services.\n\t */\n\tpublic static List<ToolCallback> toolCallbacks(AgentConfig config, PluginService pluginService,\n\t\t\tToolExecutionService toolExecutionService, McpServerService mcpServerService,\n\t\t\tAppComponentManager appComponentManager, Map<String, Object> extraParams) {\n\t\tCompositeToolCallbackProvider provider = new CompositeToolCallbackProvider(config, pluginService,\n\t\t\t\ttoolExecutionService, mcpServerService, appComponentManager, extraParams);\n\t\tToolCallback[] toolCallbacks = provider.getToolCallbacks();\n\t\tif (ArrayUtils.isEmpty(toolCallbacks)) {\n\t\t\treturn List.of();\n\t\t}\n\n\t\treturn List.of(toolCallbacks);","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/agent/tool/CompositeToolCallbackProvider.java#L113-L149","documentation":"InterruptionHook.apply() found that the supplied feedback list contained no valid Message instances (empty list, or all elements were non-Message). It logs this warning and returns an empty update map, which stops the graph and keeps it waiting for more human input instead of resuming.","triggerScenarios":"feedback instanceof List<?> and after filtering, feedbackMessages.isEmpty() — e.g. resume called with List.of(), a list of non-Message objects, or null items only.","commonSituations":"Client sending an empty array as feedback on resume; serialization dropping Message payloads; UI submitting placeholder/empty input; developers unaware items must be Message instances.","solutions":["Pass a non-empty list of Message instances (e.g. new UserMessage(\"...\")) or a plain String feedback","Validate on the client/UI that feedback is non-empty before resuming the graph","Check serialization/deserialization so Messages survive the round trip","Inspect state.getMessage() flow if resuming programmatically to ensure the update actually contains messages"],"exampleFix":"// before\nhook.apply(state, List.of()); // graph keeps waiting\n// after\nhook.apply(state, new UserMessage(\"approved, continue\"));","handlingStrategy":"validation","validationCode":"List<Message> msgs = toMessages(feedback); // wrap strings, filter nulls\nif (msgs == null || msgs.isEmpty()) {\n    throw new IllegalArgumentException(\"Feedback must contain at least one Message\");\n}","typeGuard":"boolean hasValidFeedback(Object feedback) {\n    if (feedback instanceof UserMessage || feedback instanceof String s && !s.isBlank()) return true;\n    return feedback instanceof List<?> l && l.stream().anyMatch(i -> i instanceof Message);\n}","tryCatchPattern":null,"preventionTips":["Check feedback non-empty in the UI before resuming","Normalize feedback (String/UserMessage/List<Message>) in one helper","Ensure serializers do not drop Message payloads on the resume round trip"],"tags":["interruption","human-feedback","empty-feedback"],"backgroundTag":"empty-required-field","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}