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

  1. Provide feedback (approve/reject/edit) for every approval-required tool call before resuming
  2. Match feedback by both tool name and call id
  3. 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)