alibaba/spring-ai-alibaba · error
Missing feedback for tool
Error message
Missing feedback for tool {} (id={}); waiting for human input. What it means
HumanInTheLoopHook.validateFeedback returns false (keeping the graph interrupted) when a tool call that requires approval has no matching ToolFeedback entry in InterruptionMetadata — human input for that tool call is still missing.
Solutions
- Provide feedback (approve/reject/edit) for every approval-required tool call before resuming
- Match feedback by both tool name and call id
- Return validation errors to the UI listing pending tool calls
Defensive patterns
Strategy: fallback
When it happens
Trigger: Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/hip/HumanInTheLoopHook.java:240 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/3753ffcb26235e38.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/hip/HumanInTheLoopHook.java:240
.filter(tc -> approvalOn.containsKey(tc.name()))
.toList();
// If no tool calls in this step require human approval, validation is trivially satisfied
if (toolCallsNeedingApproval.isEmpty()) {
return true;
}
// 2. For each tool call requiring approval, ensure corresponding feedback exists and its result is non-null
for (AssistantMessage.ToolCall call : toolCallsNeedingApproval) {
InterruptionMetadata.ToolFeedback matchedFeedback = toolFeedbacks.stream()
.filter(tf -> tf.getName().equals(call.name())
// Also validate id if ToolFeedback contains id field
&& call.id().equals(tf.getId()))
.findFirst()
.orElse(null);
if (matchedFeedback == null) {
log.warn("Missing feedback for tool {} (id={}); waiting for human input.",
call.name(), call.id());
return false;
}
// Ensure the feedback result is provided
if (matchedFeedback.getResult() == null) {
log.warn("Feedback result for tool {} (id={}) is null; waiting for human input.",
call.name(), call.id());
return false;
}
}
// 3. Optional: log unexpected or extra feedback entries that do not match any pending approval tool
for (InterruptionMetadata.ToolFeedback tf : toolFeedbacks) {
boolean matched = toolCallsNeedingApproval.stream()
.anyMatch(call -> call.name().equals(tf.getName()) && call.id().equals(tf.getId()));
if (!matched) {
log.warn("Ignoring unexpected tool feedback: name={}, id={}", tf.getName(), tf.getId());View on GitHub (pinned to f82da0b50f)