alibaba/spring-ai-alibaba · error · RuntimeException

${response.getResult()}

Error message

${response.getResult()}

What it means

In ToolRetryInterceptor.interceptToolCall(), if the tool handler returns a ToolCallResponse whose status is not SUCCESS, the interceptor treats it as a failure and throws RuntimeException whose message is the response result text (or "unknown error" when the result is null). This makes non-success statuses retryable like exceptions.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java:94

		String toolName = request.getToolName();

		// Check if this tool should be retried
		if (toolNames != null && !toolNames.contains(toolName)) {
			return handler.call(request);
		}

		Exception lastException = null;

		// maxAttempts counts the initial call plus retries and is always >= 1, so the tool is executed at least once and is never skipped.
		for (int attempt = 0; attempt < maxAttempts; attempt++) {
			try {
				ToolCallResponse response = handler.call(request);
				if (ToolCallResponse.SUCCESS_STATUS.equals(response.getStatus())) {
					return response;
				}
				// A non-success status is treated as a failure so it can be retried.
				String result = response.getResult() != null ? response.getResult() : "unknown error";
				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: {}",

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Fix the tool implementation to return SUCCESS status when the call actually succeeded
  2. Inspect the response.getResult() text in the exception message to identify the underlying tool error
  3. If the failure is permanent, mark the exception non-retryable via the retryOn predicate so it is rethrown immediately

Example fix

// before
return new ToolCallResponse("error: bad input", "failed");
// after
throw new IllegalArgumentException("bad input"); // or return success-status response
Defensive patterns

Strategy: try-catch

Validate before calling

ToolCallResponse resp = handler.call(req);
if (!ToolCallResponse.SUCCESS_STATUS.equals(resp.getStatus())) { log.error("Tool failed: {}", resp.getResult()); }
// configure retryOn to exclude deterministic failures
ToolRetryInterceptor.builder().retryOn(e -> !(e instanceof IllegalArgumentException)).build();

Type guard

boolean isSuccess(ToolCallResponse r) { return r != null && ToolCallResponse.SUCCESS_STATUS.equals(r.getStatus()); }

Try / catch

try { return interceptor.interceptToolCall(request, handler); } catch (RuntimeException e) { if (isRetryExhaustion(e)) return ToolCallResponse.of("tool unavailable: " + e.getMessage()); throw e; }

Prevention

When it happens

Trigger: A tool handler returns a non-SUCCESS status response, e.g. business error text returned as a result with an error status, or a result of null with a non-success status.

Common situations: Tool implementations returning error-prefixed responses instead of throwing; upstream tool API returning an error payload that the handler maps to a non-success status; null result from a failed tool.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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