alibaba/spring-ai-alibaba · error · IllegalArgumentException

Human feedback metadata must be of type InterruptionMetadata

Error message

Human feedback metadata must be of type InterruptionMetadata.

What it means

HumanInTheLoopHook.interrupt() reads the human feedback payload from RunnableConfig under HUMAN_FEEDBACK_METADATA_KEY and requires it to be an InterruptionMetadata instance. If a resume carries feedback metadata of any other type, the hook cannot validate tool-call approvals and throws IllegalArgumentException instead of silently misinterpreting the data.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/hook/hip/HumanInTheLoopHook.java:158

		else {
			log.warn("Last message is not an AssistantMessage, cannot process human feedback.");
		}

		return CompletableFuture.completedFuture(Map.of());
	}

	@Override
	public Optional<InterruptionMetadata> interrupt(String nodeId, OverAllState state, RunnableConfig config) {
		AssistantMessage lastMessage = getLastAssistantMessage(state);

		if (lastMessage == null || !lastMessage.hasToolCalls()) {
			return Optional.empty();
		}

		Optional<Object> feedback = config.metadata(RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY);
		if (feedback.isPresent()) {
			if (!(feedback.get() instanceof InterruptionMetadata)) {
				throw new IllegalArgumentException("Human feedback metadata must be of type InterruptionMetadata.");
			}

			if (!validateFeedback((InterruptionMetadata) feedback.get(), lastMessage.getToolCalls())) {
				return buildInterruptionMetadata(state, lastMessage);
			}
			return Optional.empty();
		}

		// 2. If last message is AssistantMessage
		return buildInterruptionMetadata(state, lastMessage);
	}

	private static AssistantMessage getLastAssistantMessage(OverAllState state) {
		List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());

		AssistantMessage lastMessage = null;
		for (int i = messages.size() - 1; i >= 0; i--) {
			Message msg = messages.get(i);

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Construct an InterruptionMetadata (via its builder) carrying the approved/rejected tool call ids and put that object under HUMAN_FEEDBACK_METADATA_KEY instead of a Map or String.
  2. Check the value type before resuming: if (cfg.metadata(HUMAN_FEEDBACK_METADATA_KEY).map(v -> v instanceof InterruptionMetadata).orElse(true)) proceed.
  3. Review the HumanInTheLoopHook docs/examples for the current resume payload shape after upgrading versions.

Example fix

// before
config.metadata(HUMAN_FEEDBACK_METADATA_KEY, Map.of("approved", List.of("call-1")));
// after
InterruptionMetadata feedback = InterruptionMetadata.builder()
    .approvedToolCallIds(List.of("call-1"))
    .build();
config.metadata(HUMAN_FEEDBACK_METADATA_KEY, feedback);
Defensive patterns

Strategy: type-guard

Validate before calling

Object fb = config.metadata(RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY).orElse(null);
if (fb != null && !(fb instanceof InterruptionMetadata)) {
    throw new IllegalStateException("Feedback metadata must be InterruptionMetadata, got " + fb.getClass());
}

Type guard

boolean isValidFeedback(Object o) { return o == null || o instanceof InterruptionMetadata; }

Try / catch

try {
    resume(config);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("InterruptionMetadata")) {
        // rebuild feedback payload as InterruptionMetadata and retry
    } else throw e;
}

Prevention

When it happens

Trigger: Calling resume/continue on an interrupted graph while passing config metadata under key RunnableConfig.HUMAN_FEEDBACK_METADATA_KEY with a value that is not an InterruptionMetadata object (e.g. a raw Map, String, or custom feedback DTO).

Common situations: Building custom UI resumption code that stuffs a hand-rolled feedback object into metadata; upgrading library versions where the feedback contract changed to InterruptionMetadata; copying older example code that used a plain map of approved/rejected tool call ids.

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


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/1b068dc4c695437f. Report an issue: GitHub.