alibaba/spring-ai-alibaba · warning · RuntimeException

Retry interrupted

Error message

Retry interrupted

What it means

During ModelRetryInterceptor's blocking backoff (Thread.sleep between non-streaming attempts), the thread was interrupted while sleeping. The interceptor restores the interrupt flag and rethrows RuntimeException("Retry interrupted", e) to abort the retry loop promptly.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java:107

					return modelResponse;
				}

				// Check if the response contains any exception information (exceptions captured from AgentLlmNode).
				if (message != null && message.getText() != null && message.getText().startsWith("Exception:")) {
					String exceptionText = message.getText();
					log.warn("The model call returned an exception message: {}", exceptionText);

					// Extract anomaly information from the text and determine whether a retry is possible.
					if (attempt < maxAttempts && isRetryableExceptionMessage(exceptionText)) {
						lastException = new RuntimeException(exceptionText);
						// Wait and try again
						if (currentDelay > 0) {
							try {
								log.info("Retry after {} ms", currentDelay);
								Thread.sleep(currentDelay);
							} catch (InterruptedException e) {
								Thread.currentThread().interrupt();
								throw new RuntimeException("Retry interrupted", e);
							}
						}
						// Calculate the next delay time (exponential backoff)
						currentDelay = Math.min((long) (currentDelay * backoffMultiplier), maxDelay);
						continue;
					} else if (attempt >= maxAttempts) {
						log.error("The maximum number of retries has been reached {}, and the model call has failed.", maxAttempts);
						throw new RuntimeException("Model call failed, maximum number of retries reached:" + exceptionText);
					}

					// For non-retryable exceptions, return immediately.
					return modelResponse;
				}

				// Successful response
				if (attempt > 1) {
					log.info("The model call succeeded after the {}th attempt.", attempt);
				}

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Treat this as intentional cancellation: catch RuntimeException, check that getCause() instanceof InterruptedException, and stop the work rather than retrying further.
  2. Check the interrupt flag (Thread.currentThread().isInterrupted()) around long agent runs and shut down gracefully before killing pools.
  3. Reduce initialDelay/maxDelay so backoff sleeps are short and less likely to straddle shutdown windows.
  4. If interruption is unexpected, audit which code calls interrupt()/shutdownNow() on the executing thread pool.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return retryInterceptor.interceptModel(request, handler);
} catch (RuntimeException e) {
    if ("Retry interrupted".equals(e.getMessage()) && e.getCause() instanceof InterruptedException) {
        Thread.currentThread().interrupt(); // preserve flag if re-entering interruptible code
        throw new CancellationException("Model retry cancelled");
    }
    throw e;
}

Prevention

When it happens

Trigger: interceptModel sleeping in the retry delay path (a retryable 'Exception:' message in the response, attempt < maxAttempts, currentDelay > 0) when the executing thread receives Thread.interrupt() — typically task cancellation, executor shutdown, or a timeout wrapper cancelling the call.

Common situations: Application shutdown or Spring context close while an agent call is mid-backoff; @Timed/timeout frameworks interrupting long-running calls; cancelling a Future or shutting down the thread pool running the agent graph.

Related errors


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