alibaba/spring-ai-alibaba · warning · IllegalStateException

Multiple tools with the same name (%s)

Error message

Multiple tools with the same name (%s)

What it means

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.

Source

Thrown at spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/agent/tool/CompositeToolCallbackProvider.java:131

					AppComponentTypeEnum.Workflow);
			addToolCallbacks(toolCallbacks, workflowComponentCallbacks);
		}

		return toolCallbacks.toArray(new ToolCallback[0]);
	}

	/**
	 * Validates that there are no duplicate tool names in the provided callbacks.
	 * <p>
	 * This method ensures that each tool has a unique name, which is required for proper
	 * tool resolution and execution.
	 * @param toolCallbacks the tool callbacks to validate
	 * @throws IllegalStateException if duplicate tool names are found
	 */
	private void validateToolCallbacks(ToolCallback[] toolCallbacks) {
		List<String> duplicateToolNames = ToolUtils.getDuplicateToolNames(toolCallbacks);
		if (!duplicateToolNames.isEmpty()) {
			throw new IllegalStateException(
					"Multiple tools with the same name (%s)".formatted(String.join(", ", duplicateToolNames)));
		}
	}

	/**
	 * Creates a list of tool callbacks based on the provided configuration and services.
	 */
	public static List<ToolCallback> toolCallbacks(AgentConfig config, PluginService pluginService,
			ToolExecutionService toolExecutionService, McpServerService mcpServerService,
			AppComponentManager appComponentManager, Map<String, Object> extraParams) {
		CompositeToolCallbackProvider provider = new CompositeToolCallbackProvider(config, pluginService,
				toolExecutionService, mcpServerService, appComponentManager, extraParams);
		ToolCallback[] toolCallbacks = provider.getToolCallbacks();
		if (ArrayUtils.isEmpty(toolCallbacks)) {
			return List.of();
		}

		return List.of(toolCallbacks);

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Pass a non-empty list of Message instances (e.g. new UserMessage("...")) or a plain String feedback
  2. Validate on the client/UI that feedback is non-empty before resuming the graph
  3. Check serialization/deserialization so Messages survive the round trip
  4. Inspect state.getMessage() flow if resuming programmatically to ensure the update actually contains messages

Example fix

// before
hook.apply(state, List.of()); // graph keeps waiting
// after
hook.apply(state, new UserMessage("approved, continue"));
Defensive patterns

Strategy: validation

Validate before calling

List<Message> msgs = toMessages(feedback); // wrap strings, filter nulls
if (msgs == null || msgs.isEmpty()) {
    throw new IllegalArgumentException("Feedback must contain at least one Message");
}

Type guard

boolean hasValidFeedback(Object feedback) {
    if (feedback instanceof UserMessage || feedback instanceof String s && !s.isBlank()) return true;
    return feedback instanceof List<?> l && l.stream().anyMatch(i -> i instanceof Message);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/08e1662419e7d20b. Report an issue: GitHub.