{"record":{"id":"4dd20a2dd00bc44e","repo":"alibaba/spring-ai-alibaba","slug":"conditional-multi-command-mapping-from-node","errorCode":null,"errorMessage":"Conditional multi-command mapping from node '","messagePattern":"Conditional multi-command mapping from node '","errorType":"exception","errorClass":"GraphStateException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/CompiledGraph.java","lineNumber":175,"sourceCode":"\t\t\t\t\t\t\t\te.sourceId(),\n\t\t\t\t\t\t\t\tedgeCondition,\n\t\t\t\t\t\t\t\tnodeFactories,\n\t\t\t\t\t\t\t\tkeyStrategyMap,\n\t\t\t\t\t\t\t\tcompileConfig);\n\n\t\t\t\t\t\tnodeFactories.put(conditionalParallelNode.id(), conditionalParallelNode.actionFactory());\n\t\t\t\t\t\tedges.put(e.sourceId(), new EdgeValue(conditionalParallelNode.id()));\n\n\t\t\t\t\t\t// Find parallel node targets from mappings\n\t\t\t\t\t\tvar mappedNodeIds = edgeCondition.mappings().values().stream()\n\t\t\t\t\t\t\t\t.collect(Collectors.toSet());\n\n\t\t\t\t\t\t// Validate that all mapped nodes exist in the graph\n\t\t\t\t\t\tvar missingNodeIds = mappedNodeIds.stream()\n\t\t\t\t\t\t\t\t.filter(nodeId -> !nodeFactories.containsKey(nodeId))\n\t\t\t\t\t\t\t\t.collect(Collectors.toSet());\n\t\t\t\t\t\tif (!missingNodeIds.isEmpty()) {\n\t\t\t\t\t\t\tthrow new GraphStateException(\"Conditional multi-command mapping from node '\"\n\t\t\t\t\t\t\t\t\t+ e.sourceId() + \"' references unknown target nodes: \" + missingNodeIds);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvar parallelNodeTargets = findParallelNodeTargets(mappedNodeIds);\n\t\t\t\t\t\tif (parallelNodeTargets.size() > 1) {\n\t\t\t\t\t\t\tthrow Errors.illegalMultipleTargetsOnParallelNode\n\t\t\t\t\t\t\t\t\t.exception(e.sourceId(), parallelNodeTargets);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (parallelNodeTargets.isEmpty()) {\n\t\t\t\t\t\t\tthrow Errors.illegalMultipleTargetsOnParallelNode.exception(e.sourceId(), 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Set edge from ConditionalParallelNode to the next node.\n\t\t\t\t\t\t// Conditional parallel branches currently require a single converged target.\n\t\t\t\t\t\tedges.put(conditionalParallelNode.id(), new EdgeValue(parallelNodeTargets.iterator().next()));\n\t\t\t\t\t\t// The ConditionalParallelNode will handle parallel execution internally\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Single Command action - same as regular single target edge\n\t\t\t\t\t\tedges.put(e.sourceId(), target);","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/CompiledGraph.java#L157-L193","documentation":"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.","triggerScenarios":"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.","commonSituations":"Renaming a node but not the routing map strings; conditional routes referencing nodes that only exist in another graph variant; dynamic node registration skipped.","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."],"exampleFix":"// before\n.stateGraph(\"router\", StateGraph::END) // mapping returns \"anlyzer\"\n.addNode(\"analyzer\", node)            // typo in node id\n// after\n.addNode(\"analyzer\", node)            // mapping returns \"analyzer\" consistently","handlingStrategy":"validation","validationCode":"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);","typeGuard":null,"tryCatchPattern":"try { compiled = graph.compile(config); } catch (GraphStateException e) { log.error(\"Graph validation failed: {}\", e.getMessage()); }","preventionTips":["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."],"tags":["graph-compile","conditional-edge","validation"],"backgroundTag":"invalid-state-transition","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}