{"record":{"id":"7df2f18294199a96","repo":"alibaba/spring-ai-alibaba","slug":"tool-execution-was-cancelled","errorCode":null,"errorMessage":"Tool execution was cancelled","messagePattern":"Tool execution was cancelled","errorType":"exception","errorClass":"ToolCancelledException","httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tool/AsyncToolCallback.java","lineNumber":111,"sourceCode":"\t * @throws ToolCancelledException if the operation was cancelled\n\t */\n\t@Override\n\tdefault String call(String arguments, ToolContext context) {\n\t\ttry {\n\t\t\treturn callAsync(arguments, context).join();\n\t\t}\n\t\tcatch (CompletionException e) {\n\t\t\tThrowable cause = e.getCause();\n\t\t\tif (cause instanceof RuntimeException re) {\n\t\t\t\tthrow re;\n\t\t\t}\n\t\t\tif (cause instanceof Error err) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tthrow new RuntimeException(\"Async tool execution failed\", cause);\n\t\t}\n\t\tcatch (CancellationException e) {\n\t\t\tthrow new ToolCancelledException(\"Tool execution was cancelled\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Returns whether this tool executes asynchronously.\n\t * @return true (always async for this interface)\n\t */\n\tdefault boolean isAsync() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns the timeout duration for this tool execution.\n\t * @return the timeout duration, defaults to 5 minutes\n\t */\n\tdefault Duration getTimeout() {\n\t\treturn Duration.ofMinutes(5);\n\t}","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tool/AsyncToolCallback.java#L93-L129","documentation":"When the async tool's future completes with a CancellationException (e.g. the CancellationToken was triggered), call() throws ToolCancelledException with message \"Tool execution was cancelled\". This is a typed signal that the tool was aborted rather than failed.","triggerScenarios":"A CancellationToken passed to the async tool is cancelled while the tool is still running; the underlying future is cancelled() by timeout or upstream abort; the executor cancels the task.","commonSituations":"User aborts a request mid-tool-call, agent-level timeouts cancelling running tools, cancellation propagation when a graph node is interrupted.","solutions":["Catch ToolCancelledException specifically and treat it as a normal abort, not an error.","Ensure cancellation tokens are cancelled intentionally; review timeout/cancel call sites.","Make tools check CancellationToken periodically so cancellation is honored promptly.","Log cancellation at info/debug level instead of treating it as a failure."],"exampleFix":"// before\ntry { result = asyncTool.call(input, token); }\ncatch (RuntimeException e) { log.error(\"tool failed\", e); }\n// after\ntry { result = asyncTool.call(input, token); }\ncatch (ToolCancelledException e) { log.info(\"tool cancelled, aborting gracefully\"); return null; }\ncatch (RuntimeException e) { log.error(\"tool failed\", e); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { result = callback.call(input, token); } catch (ToolCancelledException e) { /* graceful abort */ } catch (CancellationException e) { /* already wrapped, unreachable but safe */ }","preventionTips":["Treat cancellation as control flow, not failure","Wire CancellationToken through all long-running tools","Check token status before merging results","Log cancellations at info level"],"tags":["async","cancellation","tool-execution"],"backgroundTag":"request-timeout","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"}