{"record":{"id":"226a9d121413bd50","repo":"alibaba/spring-ai-alibaba","slug":"response-getresult","errorCode":null,"errorMessage":"${response.getResult()}","messagePattern":"\\$\\{response\\.getResult\\(\\)\\}","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java","lineNumber":94,"sourceCode":"\t\tString toolName = request.getToolName();\n\n\t\t// Check if this tool should be retried\n\t\tif (toolNames != null && !toolNames.contains(toolName)) {\n\t\t\treturn handler.call(request);\n\t\t}\n\n\t\tException lastException = null;\n\n\t\t// maxAttempts counts the initial call plus retries and is always >= 1, so the tool is executed at least once and is never skipped.\n\t\tfor (int attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\ttry {\n\t\t\t\tToolCallResponse response = handler.call(request);\n\t\t\t\tif (ToolCallResponse.SUCCESS_STATUS.equals(response.getStatus())) {\n\t\t\t\t\treturn response;\n\t\t\t\t}\n\t\t\t\t// A non-success status is treated as a failure so it can be retried.\n\t\t\t\tString result = response.getResult() != null ? response.getResult() : \"unknown error\";\n\t\t\t\tthrow new RuntimeException(result);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tlastException = e;\n\n\t\t\t\t// Check if we should retry this exception\n\t\t\t\tif (!retryOn.test(e)) {\n\t\t\t\t\tlog.debug(\"Exception {} not configured for retry, re-throwing\", e.getClass().getSimpleName());\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\n\t\t\t\t// Last attempt failed: Stop the operation\n\t\t\t\tif (attempt >= maxAttempts - 1) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Calculate delay\n\t\t\t\tlong delay = calculateDelay(attempt);\n\t\t\t\tlog.warn(\"Tool '{}' failed (attempt {}/{}), retrying in {}ms: {}\",","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/interceptor/toolretry/ToolRetryInterceptor.java#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the tool implementation to return SUCCESS status when the call actually succeeded","Inspect the response.getResult() text in the exception message to identify the underlying tool error","If the failure is permanent, mark the exception non-retryable via the retryOn predicate so it is rethrown immediately"],"exampleFix":"// before\nreturn new ToolCallResponse(\"error: bad input\", \"failed\");\n// after\nthrow new IllegalArgumentException(\"bad input\"); // or return success-status response","handlingStrategy":"try-catch","validationCode":"ToolCallResponse resp = handler.call(req);\nif (!ToolCallResponse.SUCCESS_STATUS.equals(resp.getStatus())) { log.error(\"Tool failed: {}\", resp.getResult()); }\n// configure retryOn to exclude deterministic failures\nToolRetryInterceptor.builder().retryOn(e -> !(e instanceof IllegalArgumentException)).build();","typeGuard":"boolean isSuccess(ToolCallResponse r) { return r != null && ToolCallResponse.SUCCESS_STATUS.equals(r.getStatus()); }","tryCatchPattern":"try { return interceptor.interceptToolCall(request, handler); } catch (RuntimeException e) { if (isRetryExhaustion(e)) return ToolCallResponse.of(\"tool unavailable: \" + e.getMessage()); throw e; }","preventionTips":["Return SUCCESS status only when the tool truly succeeded","Throw typed exceptions instead of error-status responses for permanent failures","Restrict retryOn to transient exception types","Log resp.getResult() on non-success to diagnose"],"tags":["retry","tool-call","runtime-exception"],"backgroundTag":"invalid-argument-value","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}