alibaba/spring-ai-alibaba · warning · IllegalArgumentException
extra tool param should be map
Error message
extra tool param should be map
What it means
HumanInTheLoopHook.afterModel() processes human feedback by appending ToolResponseMessages after the last model message. If the conversation's last message is not an AssistantMessage, it cannot attach tool results; it logs this warning and returns an empty update, leaving the graph to wait.
Source
Thrown at spring-ai-alibaba-admin/spring-ai-alibaba-admin-server-core/src/main/java/com/alibaba/cloud/ai/studio/core/agent/tool/ToolArgumentsHelper.java:53
* @param functionInput function input
* @param extraParams extra params
* @param toolId tool id
*/
public static Map<String, Object> mergeToolArguments(String functionInput, Map<String, Object> extraParams,
String toolId) {
Map<String, Object> arguments = ModelOptionsUtils.jsonToMap(functionInput);
if (!CollectionUtils.isEmpty(extraParams) && extraParams.containsKey(toolId)) {
Object obj = extraParams.get(toolId);
if (obj instanceof Map) {
Map<String, Object> map = (Map<String, Object>) obj;
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (!arguments.containsKey(entry.getKey())) {
arguments.put(entry.getKey(), entry.getValue());
}
}
}
else {
throw new IllegalArgumentException("extra tool param should be map");
}
}
return arguments;
}
}
View on GitHub (pinned to f82da0b50f)
Solutions
- Ensure the interrupted state is preserved exactly, with the AssistantMessage containing the pending tool calls as the last message
- Do not manually append ToolResponseMessages yourself before resuming — let the hook build them from feedback
- Verify interrupt/resume round-trip (checkpoint/serialization) keeps message order intact
- Only attach HumanInTheLoopHook to agents whose model calls can actually emit tool calls
Example fix
// before
state.withMessages(existingMessages, userMessage); // last message becomes UserMessage
// after
// resume with feedback only; keep AssistantMessage with pending tool calls as last message
hook.apply(state, new UserMessage("approved")); Defensive patterns
Strategy: type-guard
Validate before calling
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
Message last = messages.isEmpty() ? null : messages.get(messages.size() - 1);
if (!(last instanceof AssistantMessage am) || am.getToolCalls() == null || am.getToolCalls().isEmpty()) {
throw new IllegalStateException("Resume requires last message to be an AssistantMessage with pending tool calls");
} Type guard
boolean lastIsAssistantWithToolCalls(List<Message> messages) {
if (messages == null || messages.isEmpty()) return false;
return messages.get(messages.size() - 1) instanceof AssistantMessage am
&& am.getToolCalls() != null && !am.getToolCalls().isEmpty();
} Prevention
- Never mutate message order in persisted interrupt state
- Let the hook construct ToolResponseMessages; don't append them manually
- Verify checkpoint serialization preserves message order
When it happens
Trigger: afterModel (called from apply) runs while resuming from an interrupt but state.messages() last element is a ToolResponseMessage/UserMessage/SystemMessage rather than the AssistantMessage holding the pending tool calls.
Common situations: Resuming a graph that was persisted mid-way with a mutated state; custom state surgery that reordered messages; feeding feedback into a conversation whose last turn was already a tool result; human-in-the-loop hook attached to agents whose flow already answered the tool calls.
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
- Human feedback metadata must be of type InterruptionMetadata
- unknown component type: + componentType.getValue()
- INVALID_PARAMS
- user define param: + userParamParam.getField() + is empty
- failed to create dir: + saveFile.getParentFile()
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/6f7840e5345c008d.
Report an issue: GitHub.