alibaba/spring-ai-alibaba · warning
Detected SystemMessages in the message list. There should…
Error message
Detected {} SystemMessages in the message list. There should typically be only one SystemMessage. Multiple SystemMessages may cause unexpected behavior or model confusion. What it means
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.
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.
Example fix
// before messages.add(new SystemMessage(additionalInstructions)); // after messages.removeIf(m -> m instanceof SystemMessage); messages.add(0, new SystemMessage(basePrompt + "\n\n" + additionalInstructions));
Defensive patterns
Strategy: validation
Validate before calling
static void assertSingleSystemMessage(List<Message> messages) {
long n = messages.stream().filter(m -> m instanceof SystemMessage).count();
if (n > 1) throw new IllegalStateException("Multiple SystemMessages: " + n);
} Type guard
static List<Message> keepFirstSystemMessage(List<Message> messages) {
boolean seen = false;
List<Message> out = new ArrayList<>();
for (Message m : messages) {
if (m instanceof SystemMessage) {
if (seen) continue;
seen = true;
}
out.add(m);
}
return out;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- All models failed after
- Cannot find AssistantMessage before ToolResponseMessage
- ChatModel must be provided for LLM routing agent
- 解析模型调用结果出错,请重试
- Failed to get valid decision after
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/e5092f2d1955c6d7.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java:331
private List<Message> appendSystemPromptIfNeeded(ModelRequest modelRequest) {
// Create a new list and copy messages from modelRequest
List<Message> messages = new ArrayList<>(modelRequest.getMessages());
// FIXME, there should have only one SystemMessage.
// Users may have added SystemMessages in hooks or somewhere else, simply remove will cause unexpected agent behaviour.
// messages.removeIf(message -> message instanceof SystemMessage);
// Add the SystemMessage from modelRequest at the beginning if present
if (modelRequest.getSystemMessage() != null) {
messages.add(0, modelRequest.getSystemMessage());
}
long systemMessageCount = messages.stream()
.filter(message -> message instanceof SystemMessage)
.count();
if (systemMessageCount > 2) {
logger.warn("Detected {} SystemMessages in the message list. There should typically be only one SystemMessage. " +
"Multiple SystemMessages may cause unexpected behavior or model confusion.", systemMessageCount);
}
return messages;
}
/**
* Build chat options by merging toolCallbacks with the provided chatOptions.
* If chatOptions is null or not of type ToolCallingChatOptions, create a new ToolCallingChatOptions.
* If chatOptions is ToolCallingChatOptions, merge toolCallbacks (toolCallbacks takes precedence).
*
* @param chatOptions the original chat options
* @param toolCallbacks the tool callbacks to be included
* @return merged ToolCallingChatOptions
*/
@Nullable
private ToolCallingChatOptions buildChatOptions(ChatOptions chatOptions, List<ToolCallback> toolCallbacks) {
if (chatOptions == null && (toolCallbacks == null || toolCallbacks.isEmpty())) {View on GitHub (pinned to f82da0b50f)