alibaba/spring-ai-alibaba · error · IllegalArgumentException
gotoNodes cannot be empty
Error message
gotoNodes cannot be empty
What it means
MultiCommand is a record representing multiple goto targets plus state updates. Its compact constructor rejects a null or empty gotoNodes list with NullPointerException or IllegalArgumentException, because a command that goes nowhere is meaningless for graph routing.
Solutions
- Ensure the routing logic always returns at least one target node id before constructing MultiCommand
- Default to a fallback node id when the computed branch list is empty
- Use Command (single target) instead when only one destination is possible
- Validate upstream state that feeds the routing decision before building the command
Example fix
// before
return new MultiCommand(routeTargets(state), state.data()); // may be empty
// after
List<String> targets = routeTargets(state);
if (targets.isEmpty()) { targets = List.of("fallback"); }
return new MultiCommand(targets, state.data()); Defensive patterns
Strategy: validation
Validate before calling
List<String> targets = computeGotoNodes(state);
Objects.requireNonNull(targets, "gotoNodes");
if (targets.isEmpty()) throw new IllegalArgumentException("router produced no target nodes");
return new MultiCommand(targets, state.data()); Type guard
static boolean isUsable(MultiCommandInput in) { return in.gotoNodes() != null && !in.gotoNodes().isEmpty(); } Try / catch
try { return new MultiCommand(gotoNodes, update); } catch (IllegalArgumentException e) { return new MultiCommand(List.of("fallback"), update); } Prevention
- Default empty routing results to a fallback node
- Never pass Optional.empty-derived or filtered-empty lists straight into MultiCommand
- Unit-test routers for every state shape
When it happens
Trigger: Invoking new MultiCommand(List.of(), updates) or new MultiCommand(null, updates), or a node action building a MultiCommand from a router that returns no branches.
Common situations: MultiRoute node actions whose branching logic computed an empty destination list (e.g. map lookup miss, filter that removed all candidates); Java records deserialization passing empty lists.
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
- Cannot convert MultiCommand with multiple nodes to Command
- No default output or error next node provided
- Action must be either AsyncCommandAction or…
- AgentScope routing flow requires agentScopeModel in config…
- AgentScope routing flow requires at least one sub-agent
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/29696c1cb14aaba3.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/action/MultiCommand.java:35
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Represents a command that can route to multiple nodes for parallel execution.
* This is used when a conditional edge action returns multiple target nodes.
*
* @param gotoNodes A list of node identifiers to execute in parallel
* @param update A map containing key-value pairs representing updates to be merged into the current state
*/
public record MultiCommand(List<String> gotoNodes, Map<String, Object> update) {
public MultiCommand {
Objects.requireNonNull(gotoNodes, "gotoNodes cannot be null");
Objects.requireNonNull(update, "update cannot be null");
if (gotoNodes.isEmpty()) {
throw new IllegalArgumentException("gotoNodes cannot be empty");
}
}
/**
* Constructs a MultiCommand that specifies only the next nodes to transition to,
* with no state updates.
* @param gotoNodes The list of nodes to transition to. Cannot be empty.
*/
public MultiCommand(List<String> gotoNodes) {
this(gotoNodes, Map.of());
}
/**
* Checks if this MultiCommand represents a single node (for backward compatibility).
* @return true if there's only one node, false otherwise
*/
public boolean isSingleNode() {
return gotoNodes.size() == 1;View on GitHub (pinned to f82da0b50f)