flowable/flowable-engine · error · FlowableException
max wait timeout has to be positive. It was
Error message
max wait timeout has to be positive. It was
What it means
Thrown in the WaitForAnyFutureToFinishOperation constructor when a non-null timeout Duration is zero or negative. The agenda requires a strictly positive max wait duration when one is provided; a null timeout is allowed and means wait indefinitely.
Source
Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/agenda/WaitForAnyFutureToFinishOperation.java:40
import org.flowable.common.engine.api.FlowableException;
/**
* @author Filip Hrisafov
*/
public class WaitForAnyFutureToFinishOperation implements Runnable {
protected final Agenda agenda;
protected final List<ExecuteFutureActionOperation<?>> futureOperations;
protected final Duration timeout;
public WaitForAnyFutureToFinishOperation(Agenda agenda, List<ExecuteFutureActionOperation<?>> futureOperations, Duration timeout) {
this.agenda = agenda;
this.futureOperations = futureOperations;
if (timeout == null) {
this.timeout = null;
} else if (timeout.isNegative() || timeout.isZero()) {
throw new FlowableException("max wait timeout has to be positive. It was " + timeout);
} else {
this.timeout = timeout;
}
}
@Override
public void run() {
CompletableFuture[] anyOfFutures = new CompletableFuture[futureOperations.size()];
for (int i = 0; i < futureOperations.size(); i++) {
anyOfFutures[i] = futureOperations.get(i).getFuture();
}
try {
CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(anyOfFutures);
if (timeout == null) {
// This blocks until at least one is future is done
anyOfFuture.get();
} else {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Pass Duration.ofMillis(...)/ofSeconds(...) with a strictly positive amount, or pass null to wait without a timeout.
- Validate configuration before constructing: if timeout != null && timeout.isZero() || timeout.isNegative() fix the config value.
- Fix code computing remaining time so zero/negative remaining durations are treated as null (no timeout) or a minimum positive floor.
- Check for unit mistakes when converting config strings (PT0S vs PT1S).
Example fix
// before Duration timeout = Duration.between(now, deadline); // may be <= 0 new WaitForAnyFutureToFinishOperation(agenda, ops, timeout); // after Duration timeout = deadline.isAfter(now) ? Duration.between(now, deadline) : null; new WaitForAnyFutureToFinishOperation(agenda, ops, timeout);
Defensive patterns
Strategy: validation
Validate before calling
if (timeout != null && (timeout.isZero() || timeout.isNegative())) {
throw new IllegalArgumentException("max wait timeout must be positive, got " + timeout);
} Prevention
- Clamp computed durations to a positive minimum or map non-positive values to null (no timeout).
- Validate timeout configuration properties at startup.
- Watch for unit conversion producing zero durations (millis truncation).
When it happens
Trigger: Planning a WaitForAnyFutureToFinishOperation with Duration.ZERO, a negative duration (e.g. from a misconfigured property), or a computed duration that underflowed to zero.
Common situations: Configuration value parsed as '0' or 'PT0S'; a computed remaining-time duration that hit zero before the wait was scheduled; unit confusion (e.g. millis passed where seconds expected yielding 0).
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- None of the available futures completed within the max timeo
- Could not find activity ${activity} for processId ${processI
- variableName is null
- caseInstanceId is null
- The case definition id is mandatory, but '' has been provide
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/3e18d83990b0c58d.
Report an issue: GitHub.