alibaba/spring-ai-alibaba · error · GraphStateException
Conditional multi-command mapping from node '
Error message
Conditional multi-command mapping from node '
What it means
During CompiledGraph construction, conditional edges emitting multiple commands (multi-command mappings) are validated: every mapped target node must exist in the graph. If any target id has no node factory, a GraphStateException naming the source node and missing targets is thrown.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/CompiledGraph.java:175
e.sourceId(),
edgeCondition,
nodeFactories,
keyStrategyMap,
compileConfig);
nodeFactories.put(conditionalParallelNode.id(), conditionalParallelNode.actionFactory());
edges.put(e.sourceId(), new EdgeValue(conditionalParallelNode.id()));
// Find parallel node targets from mappings
var mappedNodeIds = edgeCondition.mappings().values().stream()
.collect(Collectors.toSet());
// Validate that all mapped nodes exist in the graph
var missingNodeIds = mappedNodeIds.stream()
.filter(nodeId -> !nodeFactories.containsKey(nodeId))
.collect(Collectors.toSet());
if (!missingNodeIds.isEmpty()) {
throw new GraphStateException("Conditional multi-command mapping from node '"
+ e.sourceId() + "' references unknown target nodes: " + missingNodeIds);
}
var parallelNodeTargets = findParallelNodeTargets(mappedNodeIds);
if (parallelNodeTargets.size() > 1) {
throw Errors.illegalMultipleTargetsOnParallelNode
.exception(e.sourceId(), parallelNodeTargets);
}
if (parallelNodeTargets.isEmpty()) {
throw Errors.illegalMultipleTargetsOnParallelNode.exception(e.sourceId(), 0);
}
// Set edge from ConditionalParallelNode to the next node.
// Conditional parallel branches currently require a single converged target.
edges.put(conditionalParallelNode.id(), new EdgeValue(parallelNodeTargets.iterator().next()));
// The ConditionalParallelNode will handle parallel execution internally
} else {
// Single Command action - same as regular single target edge
edges.put(e.sourceId(), target);View on GitHub (pinned to f82da0b50f)
Solutions
- Ensure every id returned by the conditional mapping is registered with graph.addNode(id, ...).
- Log or print mappedNodeIds and compare against nodeFactories keys.
- Fix typos between the routing function's returned names and node ids.
Example fix
// before
.stateGraph("router", StateGraph::END) // mapping returns "anlyzer"
.addNode("analyzer", node) // typo in node id
// after
.addNode("analyzer", node) // mapping returns "analyzer" consistently Defensive patterns
Strategy: validation
Validate before calling
Set<String> mapped = routingMap.values().stream().flatMap(Set::stream).collect(Collectors.toSet()); Set<String> missing = new HashSet<>(mapped); missing.removeAll(registeredNodeIds); if (!missing.isEmpty()) throw new IllegalStateException("Unknown routing targets: " + missing); Try / catch
try { compiled = graph.compile(config); } catch (GraphStateException e) { log.error("Graph validation failed: {}", e.getMessage()); } Prevention
- Define routing target ids as constants shared with addNode calls.
- Compile graphs in unit tests to catch mapping typos early.
- Review conditional-edge mappings after any node rename.
When it happens
Trigger: Adding a conditional edge whose edge mapping/lambda returns node ids not registered via addNode — e.g. typos in target names or targets added later/removed during refactoring.
Common situations: Renaming a node but not the routing map strings; conditional routes referencing nodes that only exist in another graph variant; dynamic node registration skipped.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/4dd20a2dd00bc44e.
Report an issue: GitHub.