{"record":{"id":"e5092f2d1955c6d7","repo":"alibaba/spring-ai-alibaba","slug":"detected-systemmessages-in-the-message-list-th","errorCode":null,"errorMessage":"Detected {} SystemMessages in the message list. There should typically be only one SystemMessage. Multiple SystemMessages may cause unexpected behavior or model confusion.","messagePattern":"Detected (.+?) SystemMessages in the message list\\. There should typically be only one SystemMessage\\. Multiple SystemMessages may cause unexpected behavior or model confusion\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java","lineNumber":331,"sourceCode":"\tprivate List<Message> appendSystemPromptIfNeeded(ModelRequest modelRequest) {\n\t\t// Create a new list and copy messages from modelRequest\n\t\tList<Message> messages = new ArrayList<>(modelRequest.getMessages());\n\n\t\t// FIXME, there should have only one SystemMessage.\n\t\t//  Users may have added SystemMessages in hooks or somewhere else, simply remove will cause unexpected agent behaviour.\n//\t\tmessages.removeIf(message -> message instanceof SystemMessage);\n\n\t\t// Add the SystemMessage from modelRequest at the beginning if present\n\t\tif (modelRequest.getSystemMessage() != null) {\n\t\t\tmessages.add(0, modelRequest.getSystemMessage());\n\t\t}\n\n\t\tlong systemMessageCount = messages.stream()\n\t\t\t\t.filter(message -> message instanceof SystemMessage)\n\t\t\t\t.count();\n\n\t\tif (systemMessageCount > 2) {\n\t\t\tlogger.warn(\"Detected {} SystemMessages in the message list. There should typically be only one SystemMessage. \" +\n\t\t\t\t\t\"Multiple SystemMessages may cause unexpected behavior or model confusion.\", systemMessageCount);\n\t\t}\n\n\t\treturn messages;\n\t}\n\n\t/**\n\t * Build chat options by merging toolCallbacks with the provided chatOptions.\n\t * If chatOptions is null or not of type ToolCallingChatOptions, create a new ToolCallingChatOptions.\n\t * If chatOptions is ToolCallingChatOptions, merge toolCallbacks (toolCallbacks takes precedence).\n\t *\n\t * @param chatOptions the original chat options\n\t * @param toolCallbacks the tool callbacks to be included\n\t * @return merged ToolCallingChatOptions\n\t */\n\t@Nullable\n\tprivate ToolCallingChatOptions buildChatOptions(ChatOptions chatOptions, List<ToolCallback> toolCallbacks) {\n\t\tif (chatOptions == null && (toolCallbacks == null || toolCallbacks.isEmpty())) {","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java#L313-L349","documentation":"AgentLlmNode.appendSystemPromptIfNeeded counts SystemMessages in the outgoing message list and warns when more than two are present. Multiple SystemMessages are unusual — most chat models expect one system prompt — and can confuse the model or be dropped/merged unpredictably by providers. This is a diagnostic warning; the message list is returned unchanged.","triggerScenarios":"Fires from appendSystemPromptIfNeeded (reachable via messages) when messages.stream().filter(m -> m instanceof SystemMessage).count() > 2 — e.g. an agent config that injects a system prompt on top of a history that already carries several SystemMessages.","commonSituations":"1) Appending per-request system prompts instead of replacing the existing one. 2) Loading persisted conversation history that contains legacy SystemMessages plus new injected prompts. 3) Multi-source prompt composition (base persona + guardrails + runtime instructions each added as a separate SystemMessage). 4) Off-by-one threshold: the code warns only above 2, so a list with exactly two SystemMessages is silently allowed.","solutions":["Deduplicate SystemMessages: keep a single system prompt and merge additional instructions into it (concatenate instead of append).","When appending the configured system prompt, first strip or replace existing SystemMessages in the history.","Sanitize persisted/restored history at load time so only one SystemMessage survives.","If multiple system blocks are intentional and your provider supports them, treat the warning as informational and document the design."],"exampleFix":"// before\nmessages.add(new SystemMessage(additionalInstructions));\n// after\nmessages.removeIf(m -> m instanceof SystemMessage);\nmessages.add(0, new SystemMessage(basePrompt + \"\\n\\n\" + additionalInstructions));","handlingStrategy":"validation","validationCode":"static void assertSingleSystemMessage(List<Message> messages) {\n    long n = messages.stream().filter(m -> m instanceof SystemMessage).count();\n    if (n > 1) throw new IllegalStateException(\"Multiple SystemMessages: \" + n);\n}","typeGuard":"static List<Message> keepFirstSystemMessage(List<Message> messages) {\n    boolean seen = false;\n    List<Message> out = new ArrayList<>();\n    for (Message m : messages) {\n        if (m instanceof SystemMessage) {\n            if (seen) continue;\n            seen = true;\n        }\n        out.add(m);\n    }\n    return out;\n}","tryCatchPattern":null,"preventionTips":["Inject the system prompt once at history position 0, replacing rather than appending","Sanitize restored/persisted conversation history on load","Merge layered instructions (persona + guardrails) into a single SystemMessage","Count SystemMessages in tests before invoking the LLM node"],"tags":["system-message","prompting","message-history","llm"],"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-17T15:17:12.973Z"}