alibaba/spring-ai-alibaba · error · IllegalStateException
Message before ToolResponseMessage is not an AssistantMessag
Error message
Message before ToolResponseMessage is not an AssistantMessage
What it means
When handling partial tool responses, handlePartialToolResponses() requires the message immediately before the ToolResponseMessage to be the AssistantMessage that issued the tool calls. If the second-to-last message is any other type (UserMessage, ToolResponseMessage, etc.), it throws IllegalStateException.
Source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentToolNode.java:419
}
return updatedState;
}
/**
* Handle partial tool responses (ToolResponseMessage branch). Supports both parallel
* and sequential execution of remaining tools.
*/
private Map<String, Object> handlePartialToolResponses(ToolResponseMessage toolResponseMessage,
List<Message> messages, OverAllState state, RunnableConfig config) {
if (messages.size() < 2) {
throw new IllegalStateException("Cannot find AssistantMessage before ToolResponseMessage");
}
Message secondLastMessage = messages.get(messages.size() - 2);
if (!(secondLastMessage instanceof AssistantMessage assistantMessage)) {
throw new IllegalStateException("Message before ToolResponseMessage is not an AssistantMessage");
}
List<ToolResponseMessage.ToolResponse> existingResponses = toolResponseMessage.getResponses();
Set<String> executedToolIds = existingResponses.stream()
.map(ToolResponseMessage.ToolResponse::id)
.collect(Collectors.toSet());
// Filter out tools that haven't been executed yet
List<AssistantMessage.ToolCall> remainingToolCalls = assistantMessage.getToolCalls()
.stream()
.filter(tc -> !executedToolIds.contains(tc.id()))
.toList();
if (remainingToolCalls.isEmpty()) {
// All tools have been executed - return empty map to avoid duplicate append
// (toolResponseMessage is already in the messages list)
return Map.of();
}View on GitHub (pinned to f82da0b50f)
Solutions
- Keep the AssistantMessage with tool calls directly before every ToolResponseMessage
- Fix message pruning/compaction so an AssistantMessage always precedes a ToolResponseMessage
- Sanitize history: merge consecutive ToolResponseMessages under one AssistantMessage
- Log messages.get(size-2).getClass() to see what is actually second-to-last
Example fix
// before List.of(toolResponse1, toolResponse2); // ToolResponseMessage precedes ToolResponseMessage // after List.of(assistantWithCalls, toolResponse1, toolResponse2);
Defensive patterns
Strategy: validation
Validate before calling
boolean sequenceValid(List<Message> msgs) { int i = msgs.size()-1; return i > 0 && msgs.get(i) instanceof ToolResponseMessage && msgs.get(i-1) instanceof AssistantMessage; } Type guard
Message requireAssistantBefore(List<Message> msgs) { Message m = msgs.get(msgs.size()-2); if (!(m instanceof AssistantMessage a)) throw new IllegalStateException("expected AssistantMessage, got " + m.getClass()); return a; } Try / catch
try { toolNode.apply(state, config); } catch (IllegalStateException e) { sanitizeHistory(state); retryOnce(); } Prevention
- Never interleave UserMessage between AssistantMessage and its ToolResponseMessage
- Merge consecutive tool-response rounds under their issuing AssistantMessage
- Audit compaction logic to preserve AssistantMessage+ToolResponseMessage pairs
When it happens
Trigger: Message history where a ToolResponseMessage is preceded by another ToolResponseMessage, a UserMessage, or a SystemMessage — e.g. two consecutive tool-response rounds with the assistant reply removed, or hand-assembled message lists.
Common situations: Manual message-list manipulation that interleaves responses; multiple parallel tool rounds where only responses were kept; prompt-compaction that kept ToolResponseMessage but dropped the AssistantMessage before it.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Cannot find AssistantMessage before ToolResponseMessage
- Last message is neither an AssistantMessage nor a ToolRespon
- Either 'instruction' or 'includeContents' must be set for Ag
- Cannot merge incompatible types: {} and {}
- Elastic search index name must be provided
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/475167c264186d0f.
Report an issue: GitHub.