alibaba/spring-ai-alibaba · warning

RoutingAgent {} attempt {}/{} returned invalid agent name: {

Error message

RoutingAgent {} attempt {}/{} returned invalid agent name: {}

What it means

RoutingEdgeAction.getDecisionWithRetry logs this warning each time an LLM routing attempt returns a decision that is not a valid sub-agent name, storing it as lastInvalidDecision for the retry feedback loop. If all retries exhaust, the final failure escalates (exception or fallback depending on code path).

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/flow/node/RoutingEdgeAction.java:221

				}

				String decisionValue = decision.agent();

				// Check if it's a valid sub-agent name
				boolean isValidAgent = subAgents.stream()
						.anyMatch(agent -> agent.name().equals(decisionValue));

				if (isValidAgent) {
					if (attempt > 0) {
						logger.info("RoutingAgent {} succeeded on retry attempt {}. Routed to sub-agent: {}",
								rootAgent.name(), attempt, decisionValue);
					}
					return decisionValue;
				}
				else {
					// Invalid agent name, store for next retry
					lastInvalidDecision = decisionValue;
					logger.warn("RoutingAgent {} attempt {}/{} returned invalid agent name: {}",
							rootAgent.name(), attempt, maxRetries, decisionValue);
				}
			}
			catch (Exception e) {
				if (attempt == maxRetries) {
					// Last attempt failed, rethrow the exception
					logger.error("RoutingAgent {} failed on final attempt {}/{}", rootAgent.name(), attempt, maxRetries, e);
					throw e;
				}
				logger.warn("RoutingAgent {} attempt {}/{} encountered an error, will retry", rootAgent.name(), attempt, maxRetries, e);
			}
		}

		// All retries exhausted
		throw new IllegalStateException(
				String.format("Failed to get valid decision after %d retries. Last invalid decision: %s",
						maxRetries, lastInvalidDecision));
	}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Make the routing prompt strict: return exactly one name from the enumerated list
  2. Add parsing that strips wrappers like 'Decision:' or JSON fences before validation
  3. Increase maxRetries and verify error feedback is appended to the retry messages
  4. Fall back to a default agent instead of failing when all attempts are invalid

Example fix

// before
String decision = llmResponse.trim(); // 'Decision: researcher' -> invalid
// after
String decision = llmResponse.replaceAll("(?i)^decision:\\s*", "").trim(); // 'researcher' -> valid
Defensive patterns

Strategy: retry

Validate before calling

String d = normalize(rawResponse);
if (!subAgents.stream().map(Agent::name).toList().contains(d)) { /* append corrective feedback and retry */ }

Type guard

static boolean isKnownAgentName(String s, List<Agent> agents) { return s != null && agents.stream().anyMatch(a -> a.name().equals(s)); }

Try / catch

try { String d = getDecisionWithRetry(state); } catch (Exception e) { if (e is last-retry failure) { routeToDefaultAgent(); } else { throw e; } }

Prevention

When it happens

Trigger: decisionValue parses the LLM response but the value is not among the sub-agent names, on any attempt up to maxRetries; the message includes the attempt number, max, and the invalid name.

Common situations: Model returns 'END'/'none'/explanatory text instead of an agent name; agent list changed but prompt/cache stale; model hallucinating names from similar tasks; non-JSON wrapping like 'Decision: researcher'.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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