flowable/flowable-engine · error · FlowableException

Future was interrupted

Error message

Future was interrupted

What it means

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.

Source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/agenda/WaitForAnyFutureToFinishOperation.java:76

            } else {
                try {
                    // This blocks until at least one is future is done or the timeout is reached
                    anyOfFuture.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
                } catch (TimeoutException e) {
                    // When the timeout is reached we need to cancel all the futures that are not done
                    for (ExecuteFutureActionOperation<?> futureOperation : futureOperations) {
                        if (!futureOperation.isDone()) {
                            // If there was a timeout then we need to cancel all the futures that have not completed already
                            futureOperation.getFuture().cancel(true);
                        }
                    }
                    throw new FlowableException("None of the available futures completed within the max timeout of " + timeout, e);
                }
            }

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new FlowableException("Future was interrupted", e);
        } catch (ExecutionException e) {
            // If there was any exception then it will be handled by the appropriate action
        }

        // Now go through future operation and schedule them for execution if they are done
        for (ExecuteFutureActionOperation<?> futureOperation : futureOperations) {
            if (futureOperation.isDone()) {
                // If it is done then schedule it for execution
                agenda.planOperation(futureOperation);
            } else {
                // Otherwise plan a new future operation
                agenda.planFutureOperation((CompletableFuture<Object>) futureOperation.getFuture(),
                        (BiConsumer<Object, Throwable>) futureOperation.getAction());
            }
        }
    }
}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure shutdown hooks complete or cancel pending operations before interrupting worker threads.
  2. Use bounded future.get timeouts so waits finish naturally instead of relying on interrupts.
  3. Keep the async executor healthy so futures complete promptly, minimizing the window where interrupts can land.
  4. Wrap the operation in try-catch of FlowableException and check Thread.currentThread().isInterrupted() to react to cancellation.

Example fix

// before
thread.interrupt(); // during pending agenda wait
// after
operation.getFuture().cancel(false); // cancel the work, not the thread
Defensive patterns

Strategy: try-catch

Try / catch

try {
  waitForOperation.run();
} catch (FlowableException e) {
  if ("Future was interrupted".equals(e.getMessage())) {
    Thread.currentThread().interrupt();
    return; // treat as cancellation
  }
  throw e;
}

Prevention

When it happens

Trigger: External interrupt of the executing thread while blocked waiting for future operations (executor shutdown, cancellation, watchdog timeout).

Common situations: Graceful shutdown of the process engine while async waits are pending; container stop/redeploy; timeouts that interrupt worker threads.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/5800195354b79fd1. Report an issue: GitHub.