{"record":{"id":"5800195354b79fd1","repo":"flowable/flowable-engine","slug":"future-was-interrupted-580019","errorCode":null,"errorMessage":"Future was interrupted","messagePattern":"Future was interrupted","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/agenda/WaitForAnyFutureToFinishOperation.java","lineNumber":76,"sourceCode":"            } else {\n                try {\n                    // This blocks until at least one is future is done or the timeout is reached\n                    anyOfFuture.get(timeout.toMillis(), TimeUnit.MILLISECONDS);\n                } catch (TimeoutException e) {\n                    // When the timeout is reached we need to cancel all the futures that are not done\n                    for (ExecuteFutureActionOperation<?> futureOperation : futureOperations) {\n                        if (!futureOperation.isDone()) {\n                            // If there was a timeout then we need to cancel all the futures that have not completed already\n                            futureOperation.getFuture().cancel(true);\n                        }\n                    }\n                    throw new FlowableException(\"None of the available futures completed within the max timeout of \" + timeout, e);\n                }\n            }\n\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new FlowableException(\"Future was interrupted\", e);\n        } catch (ExecutionException e) {\n            // If there was any exception then it will be handled by the appropriate action\n        }\n\n        // Now go through future operation and schedule them for execution if they are done\n        for (ExecuteFutureActionOperation<?> futureOperation : futureOperations) {\n            if (futureOperation.isDone()) {\n                // If it is done then schedule it for execution\n                agenda.planOperation(futureOperation);\n            } else {\n                // Otherwise plan a new future operation\n                agenda.planFutureOperation((CompletableFuture<Object>) futureOperation.getFuture(),\n                        (BiConsumer<Object, Throwable>) futureOperation.getAction());\n            }\n        }\n    }\n}\n","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/agenda/WaitForAnyFutureToFinishOperation.java#L58-L94","documentation":"Thrown by WaitForAnyFutureToFinishOperation.run when the thread waiting on futures receives Thread.interrupt(). The interrupt status is restored and rethrown as a FlowableException('Future was interrupted'). This mirrors ExecuteFutureActionOperation's interrupt handling but occurs in the aggregation/waiting operation.","triggerScenarios":"External interrupt of the executing thread while blocked waiting for future operations (executor shutdown, cancellation, watchdog timeout).","commonSituations":"Graceful shutdown of the process engine while async waits are pending; container stop/redeploy; timeouts that interrupt worker threads.","solutions":["Ensure shutdown hooks complete or cancel pending operations before interrupting worker threads.","Use bounded future.get timeouts so waits finish naturally instead of relying on interrupts.","Keep the async executor healthy so futures complete promptly, minimizing the window where interrupts can land.","Wrap the operation in try-catch of FlowableException and check Thread.currentThread().isInterrupted() to react to cancellation."],"exampleFix":"// before\nthread.interrupt(); // during pending agenda wait\n// after\noperation.getFuture().cancel(false); // cancel the work, not the thread","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  waitForOperation.run();\n} catch (FlowableException e) {\n  if (\"Future was interrupted\".equals(e.getMessage())) {\n    Thread.currentThread().interrupt();\n    return; // treat as cancellation\n  }\n  throw e;\n}","preventionTips":["Cancel individual futures instead of interrupting the executing thread.","Drain pending operations during shutdown before interrupting workers.","Use bounded waits so operations finish without needing interrupts.","Keep async executors responsive to minimize interrupt exposure."],"tags":["concurrency","interrupt","future","agenda"],"backgroundTag":"operation-timeout","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}