{"record":{"id":"cc6f78288611c699","repo":"alibaba/spring-ai-alibaba","slug":"no-messages-found-in-state-when-routing-from-model","errorCode":null,"errorMessage":"No messages found in state when routing from model to tools","messagePattern":"No messages found in state when routing from model to tools","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/ReactAgent.java","lineNumber":788,"sourceCode":"\t\t\t\t\tjumpTo = (JumpTo) jumpToValue;\n\t\t\t\t} else if (jumpToValue instanceof String) {\n\t\t\t\t\tjumpTo = JumpTo.fromStringOrNull((String) jumpToValue);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// If a valid jump_to instruction exists, execute it immediately\n\t\t\t\tif (jumpTo != null) {\n\t\t\t\t\treturn switch (jumpTo) {\n\t\t\t\t\t\tcase model -> modelDestination;\n\t\t\t\t\t\tcase end -> endDestination;\n\t\t\t\t\t\tcase tool -> AGENT_TOOL_NAME;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// Priority 2: Check message content for tool calls\n\t\t\tList<Message> messages = (List<Message>) state.value(\"messages\").orElse(List.of());\n\t\t\tif (messages.isEmpty()) {\n\t\t\t\tlogger.warn(\"No messages found in state when routing from model to tools\");\n\t\t\t\treturn endDestination;\n\t\t\t}\n\t\t\tMessage lastMessage = messages.get(messages.size() - 1);\n\n\t\t\t// 1. Check the last message type\n\t\t\tif (lastMessage instanceof AssistantMessage assistantMessage) {\n\t\t\t\t// 2. If last message is AssistantMessage\n\t\t\t\tif (assistantMessage.hasToolCalls()) {\n\t\t\t\t\treturn AGENT_TOOL_NAME;\n\t\t\t\t} else {\n\t\t\t\t\treturn endDestination;\n\t\t\t\t}\n\t\t\t} else if (lastMessage instanceof ToolResponseMessage) {\n\t\t\t\t// 3. If last message is ToolResponseMessage\n\t\t\t\tif (messages.size() < 2) {\n\t\t\t\t\t// Should not happen in a valid ReAct loop, but as a safeguard.\n\t\t\t\t\tthrow new RuntimeException(\"Less than 2 messages in state when last message is ToolResponseMessage\");\n\t\t\t\t}","sourceCodeStart":770,"sourceCodeEnd":806,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/ReactAgent.java#L770-L806","documentation":"ReactAgent's model-to-tools routing edge reads the 'messages' key from graph state to inspect the last message's tool calls. If the state has no messages at routing time, it logs this warning and routes to the END destination instead of the tools node, ending the run.","triggerScenarios":"makeModelToTools routing function executes while state.value(\"messages\") is empty — the model node produced no messages into state before the conditional edge was evaluated.","commonSituations":"Custom state graph replacing the default messages key; model node writing output to a different key (e.g. via outputKey) so 'messages' stays empty; state reducer misconfigured; resuming from a checkpoint that lacks messages.","solutions":["Ensure the model node stores its AssistantMessage under the standard 'messages' state key","Do not set an outputKey on the ReactAgent that diverts messages away from 'messages' when tool routing is used","Verify checkpoints/state include 'messages' when resuming","Add a guard in custom graphs to initialize 'messages' before routing"],"exampleFix":"// before\nnode writes: state.set(\"model_output\", assistantMessage); // 'messages' stays empty\n// after\nnode appends: new AppendableValue<>(\"messages\", assistantMessage); // routing sees the message","handlingStrategy":"validation","validationCode":"List<Message> messages = (List<Message>) state.value(\"messages\").orElse(List.of());\nif (messages.isEmpty()) { throw new IllegalStateException(\"'messages' state key must be populated before model-to-tools routing\"); }","typeGuard":"static boolean hasMessages(Map<String,Object> state) { Object m = state.get(\"messages\"); return m instanceof List<?> l && !l.isEmpty(); }","tryCatchPattern":null,"preventionTips":["Keep the standard 'messages' key as the sole message channel when using ReactAgent tool routing","Avoid setting outputKey on ReactAgents whose routing depends on 'messages'","Test checkpoint/resume paths to confirm 'messages' round-trips through persistence"],"tags":["state-routing","messages","react-agent"],"backgroundTag":"empty-required-field","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}