alibaba/spring-ai-alibaba · error · RuntimeException
Model call failed (non-retryable exception)
Error message
Model call failed (non-retryable exception)
What it means
ModelRetryInterceptor caught an exception from the model call whose retryableExceptionPredicate returned false, meaning the failure is not transient (per the default: no I/O/connection/timeout/handshake/socket wording, no ResourceAccessException/WebClientRequestException, no IO/Socket/Connect/Timeout/SSL cause). It rethrows immediately as RuntimeException("Model call failed (non-retryable exception)", e) instead of retrying.
Source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/modelretry/ModelRetryInterceptor.java:140
// Successful response
if (attempt > 1) {
log.info("The model call succeeded after the {}th attempt.", attempt);
}
return modelResponse;
} catch (Exception e) {
lastException = e;
log.warn("Model call failed (attempted {}/{}): {}", attempt, maxAttempts, e.getMessage());
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.", lastException);
}
// Determine if an exception can be retried.
if (!retryableExceptionPredicate.test(e)) {
log.warn("Exceptions cannot be retried and are thrown immediately: {}", e.getMessage());
throw new RuntimeException("Model call failed (non-retryable exception)", e);
}
// Wait and try again
if (currentDelay > 0) {
try {
log.info("Retry after {} ms", currentDelay);
Thread.sleep(currentDelay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Retry interrupted", ie);
}
}
// Calculate the next delay time (exponential backoff)
currentDelay = Math.min((long) (currentDelay * backoffMultiplier), maxDelay);
}
}
View on GitHub (pinned to f82da0b50f)
Solutions
- Read the cause — this is a deterministic error; fix the request (auth, model name, prompt size) rather than retrying.
- If the error really is transient, extend the predicate via retryableExceptionPredicate(...) to match its type/message.
- Verify credentials and request payload against the provider's API docs.
- Confirm default retryability: ResourceAccessException/WebClientRequestException and IO/Socket/Connect/Timeout/SSL causes are retryable; everything else is not.
Example fix
// before: default predicate misses provider-specific retryables
ModelRetryInterceptor.builder().build();
// after
ModelRetryInterceptor.builder()
.retryableExceptionPredicate(e ->
e instanceof io.netty.channel.ConnectTimeoutException
|| e.getMessage().contains("Too Many Requests"))
.build(); Defensive patterns
Strategy: try-catch
Validate before calling
// Validate request shape before invoking (avoids deterministic 4xx failures)
Objects.requireNonNull(prompt, "prompt");
if (tokenEstimator.count(prompt) > maxContextTokens) throw new IllegalArgumentException("Prompt exceeds model context window"); Try / catch
try {
return retryInterceptor.interceptModel(request, handler);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("non-retryable exception")) {
Throwable root = e.getCause();
// e.g. 401 -> refresh credentials; 400 -> fix request; do not retry
throw new PermanentModelCallException(root);
}
throw e;
} Prevention
- Tune retryableExceptionPredicate to your provider's actual exception taxonomy (Transient vs Permanent).
- Validate prompts (token limits, message format) before the call to avoid deterministic 400s.
- Rotate/refresh API keys proactively; auth errors are never retried.
When it happens
Trigger: interceptModel where handler.call throws an exception that the configured predicate classifies as non-retryable — e.g. HTTP 400 invalid request, 401 authentication failure, context-length errors, or exceptions whose message/cause chain matches none of the retryable keywords.
Common situations: Expired or wrong API key (401); malformed prompt exceeding token limits (400); unknown model name; serializing unsupported message content; a custom retryableExceptionPredicate too narrow to match your provider's exception types.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- Exceptions cannot be retried and are thrown immediately: {}
- INVALID_PARAMS
- failed to create index
- WORKFLOW_CONFIG_INVALID
- 解析模型调用结果出错,请重试
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/47768bf3f7456e10.
Report an issue: GitHub.