alibaba/spring-ai-alibaba · error · IllegalStateException
Last message is neither an AssistantMessage nor a ToolRespon
Error message
Last message is neither an AssistantMessage nor a ToolResponseMessage
What it means
AgentToolNode.apply() inspects the last message in state to decide how to execute tool calls. It only understands AssistantMessage (carries tool calls) and ToolResponseMessage (partial responses); anything else leaves the node with nothing actionable, so it throws IllegalStateException.
Source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentToolNode.java:178
if (enableActingLog) {
logger.info("[ThreadId {}] Agent {} acting with {} tools.", config.threadId().orElse(THREAD_ID_DEFAULT),
agentName, toolCalls.size());
}
// Choose execution mode based on configuration
if (parallelToolExecution && toolCalls.size() > 1) {
return executeToolCallsParallel(toolCalls, state, config);
}
else {
return executeToolCallsSequential(toolCalls, state, config);
}
}
else if (lastMessage instanceof ToolResponseMessage toolResponseMessage) {
return handlePartialToolResponses(toolResponseMessage, messages, state, config);
}
else {
throw new IllegalStateException("Last message is neither an AssistantMessage nor a ToolResponseMessage");
}
}
/**
* Sequential execution of tool calls (original behavior).
*
* <p>
* Each tool gets its own isolated state update map. This prevents a subsequent tool's
* timeout from clearing state updates from previously successful tools. The isolation
* is achieved by:
* <ol>
* <li>Creating a new {@code ConcurrentHashMap} for each tool execution</li>
* <li>Immediately merging successful updates into {@code mergedUpdates}</li>
* <li>If a tool times out, only its isolated map is cleared (line 549), not the
* already-merged data</li>
* </ol>
* </p>
*View on GitHub (pinned to f82da0b50f)
Solutions
- Ensure an AgentLlmNode (LLM call with tools) precedes the tool node so an AssistantMessage is produced
- Verify the model actually returned tool calls; if it answered in plain text, route to an end node instead of the tool node
- Check any message-pruning/compaction logic so the AssistantMessage is retained
- Log the last message type before the tool node to identify what is actually in state
Example fix
// before
state.add("messages", List.of(new UserMessage("do it"))); // tool node throws
// after
// route: llmNode -> toolNode, so the last message is an AssistantMessage with tool calls Defensive patterns
Strategy: validation
Validate before calling
Message last = messages.get(messages.size()-1); boolean ok = last instanceof AssistantMessage || last instanceof ToolResponseMessage;
Type guard
boolean toolNodeCanProcess(List<Message> msgs) { Message m = msgs.get(msgs.size()-1); return m instanceof AssistantMessage || m instanceof ToolResponseMessage; } Try / catch
try { toolNode.apply(state, config); } catch (IllegalStateException e) { log.warn("No tool-callable last message: {}", last.getClass()); routeToEndNode(); } Prevention
- Always place an LLM node with bound tools before the tool node
- Check the model response: plain-text answers without tool calls should route to a finish node
- Avoid pruning the trailing AssistantMessage from history
When it happens
Trigger: The state's "messages" list ends with a UserMessage, SystemMessage, or custom Message subclass when the tool node runs — e.g. the LLM produced no tool-call response, or messages were reordered/pruned upstream.
Common situations: Running the tool node without a preceding LLM node in the graph; a model returning plain text instead of tool calls while tools are bound; custom message filtering that drops the AssistantMessage; interrupt/resume logic that trimmed history.
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
- Message before ToolResponseMessage is not an AssistantMessag
- 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/8499dba73dcfefa7.
Report an issue: GitHub.