alibaba/spring-ai-alibaba · error · RuntimeException

Failed to check interruptAfter hook for streaming node

Error message

Failed to check interruptAfter hook for streaming node

What it means

NodeExecutor wraps any exception thrown while evaluating the interruptAfter hook of a streaming node into a RuntimeException with this message. The interruptAfter mechanism lets a graph pause after a node completes so the caller can inspect or modify state; a failure here aborts node execution. The original exception is preserved as the cause.

Source

Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/executor/NodeExecutor.java:844

	 * @return interruption metadata if the hook triggers
	 */
	private Optional<InterruptionMetadata> interruptAfterForStreaming(GraphRunnerContext context,
			Map<String, Object> actionResult) {
		String currentNodeId = context.getCurrentNodeId();
		AsyncNodeActionWithConfig action = context.getNodeAction(currentNodeId);

		if (!(action instanceof InterruptableAction interruptableAction)) {
			return Optional.empty();
		}

		try {
			OverAllState stateBeforeMerge = context.cloneState(context.getCurrentStateData());
			return interruptableAction.interruptAfter(currentNodeId, stateBeforeMerge, actionResult,
					context.getConfig());
		}
		catch (Exception e) {
			context.doListeners(ERROR, e);
			throw new RuntimeException("Failed to check interruptAfter hook for streaming node", e);
		}
	}

	/**
	 * Handles ParallelGraphFlux processing with node ID preservation for all parallel streams.
	 * @param context the graph runner context
	 * @param parallelGraphFlux the ParallelGraphFlux to handle
	 * @param partialState the partial state
	 * @param resultValue the atomic reference to store the result value
	 * @return Flux of GraphResponse with ParallelGraphFlux handling result
	 */
	private Flux<GraphResponse<NodeOutput>> handleParallelGraphFlux(GraphRunnerContext context,
																	ParallelGraphFlux parallelGraphFlux, Map<String, Object> partialState,
																	AtomicReference<Object> resultValue) throws Exception {

		if (parallelGraphFlux.isEmpty()) {
			// Handle empty ParallelGraphFlux
			return handleNonStreamingResult(context, partialState, resultValue);

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Inspect the cause chain (getCause()) to find the real exception from the interruptAfter hook or cloneState.
  2. Fix the custom InterruptAfterAction implementation to handle null/empty state fields defensively.
  3. Verify state values are cloneable/serializable if using checkpointed state with streaming nodes.
  4. Check registered graph listeners for exceptions thrown during the ERROR/After-node lifecycle events.
  5. Temporarily remove interruptAfter from the CompileConfig to confirm the hook is the failure point.

Example fix

// before: hook assumes state value is always present
return state.value("draft").map(...);
// after: guard against missing value
Object draft = state.value("draft").orElse(null);
if (draft == null) { return Optional.empty(); }
return Optional.of(new InterruptMetadata(nodeId, draft));
Defensive patterns

Strategy: try-catch

Validate before calling

// Before compiling, sanity-check the hook compiles/returns
InterruptAfterAction hook = ...;
try { hook.interruptAfter(nodeId, state, result, config); } catch (Exception e) { log.error("hook precheck failed", e); }

Type guard

boolean safeState(OverAllState s) { return s != null && s.data() != null; }

Try / catch

try { graph.invoke(inputs, config); } catch (RuntimeException e) { if (e.getMessage() != null && e.getMessage().contains("interruptAfter hook")) { Throwable root = e.getCause(); /* handle hook failure */ } }

Prevention

When it happens

Trigger: An exception is thrown by interruptableAction.interruptAfter(currentNodeId, stateBeforeMerge, actionResult, config) — e.g. a user-supplied InterruptAfterAction fails, or context.cloneState(context.getCurrentStateData()) throws while snapshotting state before the hook.

Common situations: Custom interrupt/human-in-the-loop hooks with bugs (NPEs on null state fields); state classes that are not properly cloneable/serializable in streaming mode; listener registered via doListeners throwing; concurrent state mutation during streaming execution.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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