alibaba/spring-ai-alibaba · warning
Tool '{}' failed (attempt {}/{}), retrying in {}ms: {}
Error message
Tool '{}' failed (attempt {}/{}), retrying in {}ms: {} What it means
ToolRetryInterceptor logs this warning each time a tool call fails but is still eligible for another attempt. It reports the tool name, attempt count out of maxAttempts, the computed backoff delay, and the failure message. Tool execution continues after the sleep; only non-retryable exceptions or exhausted attempts terminate the loop.
Source
Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java:112
throw new RuntimeException(result);
}
catch (Exception e) {
lastException = e;
// Check if we should retry this exception
if (!retryOn.test(e)) {
log.debug("Exception {} not configured for retry, re-throwing", e.getClass().getSimpleName());
throw e;
}
// Last attempt failed: Stop the operation
if (attempt >= maxAttempts - 1) {
break;
}
// Calculate delay
long delay = calculateDelay(attempt);
log.warn("Tool '{}' failed (attempt {}/{}), retrying in {}ms: {}",
toolName, attempt + 1, maxAttempts, delay, e.getMessage());
try {
Thread.sleep(delay);
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Retry interrupted", ie);
}
}
}
// All retries exhausted
if (onFailure == OnFailureBehavior.RAISE) {
throw new RuntimeException("Tool call failed after " + maxAttempts + " attempts", lastException);
}
else {
// Return error message as tool responseView on GitHub (pinned to f82da0b50f)
Solutions
- Read the exception message/detail in the warning to fix the underlying tool failure (endpoint down, timeout too low, dependency missing).
- Tune maxAttempts and the backoff (calculateDelay) so retries stop earlier for persistently failing tools.
- Configure a non-retryable exception predicate so deterministic failures (bad tool arguments, permission errors) rethrow immediately instead of retrying.
- Add resilience inside the tool itself (timeouts, circuit breaker) rather than relying on interceptor-level retries.
Example fix
// before
new ToolRetryInterceptor(5) // retries everything 5 times
// after
new ToolRetryInterceptor(3)
.retryableExceptionPredicate(e -> e instanceof java.io.IOException || e instanceof java.util.concurrent.TimeoutException) Defensive patterns
Strategy: retry
Validate before calling
if (maxAttempts < 1 || maxAttempts > 10) {
throw new IllegalArgumentException("maxAttempts must be between 1 and 10");
} Try / catch
try {
return interceptToolCall(req, handler);
} catch (RuntimeException e) {
log.warn("Tool {} exhausted retries: {}", req.getToolName(), e.getMessage());
return ToolCallResponse.builder().status("error").content("Tool unavailable").build();
} Prevention
- Bound maxAttempts and backoff delay explicitly
- Classify exceptions: retry only transient ones
- Add timeouts inside the tool itself
- Alert on repeated retry warnings for the same tool
When it happens
Trigger: interceptToolCall catches an exception from the underlying ToolCallback, attempt < maxAttempts - 1, and the exception passes the retryable check; the warning fires before Thread.sleep(delay) for each failed intermediate attempt.
Common situations: 1) A tool invoking a flaky HTTP endpoint that intermittently times out. 2) A database-backed tool hitting transient connection pool exhaustion. 3) A shell/MCP tool crashing intermittently under load. 4) maxAttempts set high so users see this warning repeatedly while an endpoint is down.
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
- Retry interrupted
- The backoffMultiplier must be >= 1.0
- ${response.getResult()}
- Tool call failed after ${maxAttempts} attempts
- The model call returned an exception message: {}
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/168c75034fbcd592.
Report an issue: GitHub.