alibaba/spring-ai-alibaba · error
RoutingAgent exhausted routing retries. Routing to fallback…
Error message
RoutingAgent {} exhausted routing retries. Routing to fallback agent {}. What it means
RoutingNode.apply, after getDecisionWithRetry exhausts DEFAULT_MAX_RETRIES without a valid RoutingDecision: if a fallback agent is configured it logs this warning and routes there; the message marks degradation from LLM-based routing to the fallback path.
Solutions
- Configure a fallback agent so exhausted retries degrade gracefully
- Improve the routing system prompt and available-agent descriptions
- Increase DEFAULT_MAX_RETRIES or loosen decision validation
- Check the ChatModel for malformed/empty structured outputs
Defensive patterns
Strategy: fallback
When it happens
Trigger: Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/flow/node/RoutingNode.java:113 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/3c0963b46d6d4a37.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/flow/node/RoutingNode.java:113
this.chatClient = ChatClient.builder(chatModel).defaultSystem(sb.toString()).build();
}
@Override
public MultiCommand apply(OverAllState state, RunnableConfig config) throws Exception {
@SuppressWarnings("unchecked")
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
// Prepare messages with instruction if available
List<Message> messagesWithInstruction = prepareMessagesWithInstruction(messages);
RoutingDecision decision;
try {
decision = getDecisionWithRetry(messagesWithInstruction, DEFAULT_MAX_RETRIES);
}
catch (Exception e) {
MultiCommand fallbackCommand = createFallbackCommand(state, messages);
if (fallbackCommand != null) {
logger.warn("RoutingAgent {} exhausted routing retries. Routing to fallback agent {}.",
rootAgent.name(), fallbackCommand.gotoNodes().get(0));
return fallbackCommand;
}
throw e;
}
List<String> decisionValues = decision.getAgentNames();
// Validate all agent names are valid
List<String> invalidAgents = decisionValues.stream()
.filter(agentName -> subAgents.stream().noneMatch(agent -> agent.name().equals(agentName)))
.collect(Collectors.toList());
if (invalidAgents.isEmpty() && !decisionValues.isEmpty()) {
if (decisionValues.size() == 1) {
logger.info("RoutingAgent {} routed to single sub-agent {}.", rootAgent.name(), decisionValues.get(0));
} else {
logger.info("RoutingAgent {} routed to {} sub-agents in parallel: {}.",
rootAgent.name(), decisionValues.size(), String.join(", ", decisionValues));View on GitHub (pinned to f82da0b50f)