{"record":{"id":"ac7c32d194d3de6a","repo":"alibaba/spring-ai-alibaba","slug":"interrupted-while-waiting-for-execution-slot","errorCode":null,"errorMessage":"Interrupted while waiting for execution slot","messagePattern":"Interrupted while waiting for execution slot","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/internal/node/ParallelNode.java","lineNumber":426,"sourceCode":"\t\t\t\t\t\t\tactualNodeId, semaphore.availablePermits());\n\t\t\t\t\tsemaphore.acquire();\n\t\t\t\t\tlogger.debug(\"Node {} acquired semaphore permit. Remaining permits: {}\",\n\t\t\t\t\t\t\tactualNodeId, semaphore.availablePermits());\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlogger.debug(\"Executing task for node {} in thread {} with concurrency control\",\n\t\t\t\t\t\t\t\tactualNodeId, Thread.currentThread().getName());\n\t\t\t\t\t\treturn evalNodeActionSync(action, actualNodeId, state, config).join();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\t// Always release the semaphore permit\n\t\t\t\t\t\tsemaphore.release();\n\t\t\t\t\t\tlogger.debug(\"Node {} released semaphore permit. Available permits: {}\",\n\t\t\t\t\t\t\t\tactualNodeId, semaphore.availablePermits());\n\t\t\t\t\t}\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t\tlogger.error(\"Node {} was interrupted while waiting for semaphore\", actualNodeId, e);\n\t\t\t\t\tthrow new RuntimeException(\"Interrupted while waiting for execution slot\", e);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlogger.error(\"Error executing task for node {}\", actualNodeId, e);\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}, executor);\n\t\t}\n\n\t\t@Override\n\t\tpublic CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {\n\t\t\t// Get maxConcurrency from config metadata\n\t\t\tInteger maxConcurrency = config.metadata(formatMaxConcurrencyKey(nodeId))\n\t\t\t\t\t.filter(value -> value instanceof Integer)\n\t\t\t\t\t.map(Integer.class::cast)\n\t\t\t\t\t.orElse(null);\n\n\t\t\t// Create semaphore for concurrency control if maxConcurrency is set\n\t\t\tSemaphore semaphore = maxConcurrency != null ? new Semaphore(maxConcurrency) : null;\n","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/internal/node/ParallelNode.java#L408-L444","documentation":"evalNodeActionWithSemaphore waits on a Semaphore to acquire an execution slot before running a parallel branch. If the waiting thread is interrupted, it restores the interrupt flag, logs, and throws RuntimeException(\"Interrupted while waiting for execution slot\", e). This preserves cancellation semantics while converting the checked InterruptedException into an unchecked one for the CompletableFuture pipeline.","triggerScenarios":"Thread interrupt delivered while blocked in semaphore.acquire() — typically when the enclosing CompletableFuture is cancelled, the overall graph run is cancelled/times out, or the JVM/shutdown hooks interrupt worker threads.","commonSituations":"Cancelling a graph execution from another thread; application shutdown during a long-running parallel node; timeout wrappers that cancel futures and interrupt executors.","solutions":["Check whether your application or framework intentionally cancelled the execution and handle cancellation gracefully","Avoid interrupting executor threads; use cancellation-aware timeouts instead","Increase semaphore permits if waits are long and timeouts are triggering cancellation","Catch the exception in the caller and treat it as cancellation, not a code bug"],"exampleFix":"// before\nfuture.get(30, TimeUnit.SECONDS); // cancels & interrupts branches on timeout\n// after\ntry {\n    future.get(30, TimeUnit.SECONDS);\n} catch (ExecutionException e) {\n    if (e.getCause() instanceof RuntimeException re && \"Interrupted while waiting for execution slot\".equals(re.getMessage())) {\n        logger.info(\"parallel branch cancelled by timeout\");\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    node.apply(state, config).join();\n} catch (CompletionException e) {\n    if (e.getCause() instanceof RuntimeException re\n            && re.getCause() instanceof InterruptedException) {\n        // treat as cancellation, not failure\n    }\n}","preventionTips":["Design cancellation handling before cancelling graph futures","Avoid aggressive timeout+cancel patterns around parallel nodes","Size the semaphore generously so waits are short","Shut down executors only after all graph runs complete"],"tags":["interrupted","semaphore","cancellation","parallel-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-14T11:17:12.474Z"}