flowable/flowable-engine · error · FlowableIllegalStateException
Invocation result + invocationResult + from invocation + inv
Error message
Invocation result + invocationResult + from invocation + invocation + was not a CompletableFuture
What it means
ServiceTaskDelegateExpressionActivityBehavior.execute throws FlowableIllegalStateException when a delegate expression resolves to a FutureJavaDelegate whose invocation result is not a CompletableFuture. FutureJavaDelegate contract requires the delegate's invocation to return a CompletableFuture so Flowable can plan the async completion; anything else breaks the contract.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java:199
}
if (loggingSessionEnabled) {
BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_EXIT,
"Executed service task with delegate " + delegate, execution);
}
} else if (delegate instanceof FutureJavaDelegate) {
FutureJavaDelegate<Object> futureJavaDelegate = (FutureJavaDelegate<Object>) delegate;
DelegateInvocation invocation = new FutureJavaDelegateInvocation(futureJavaDelegate, execution,
processEngineConfiguration.getAsyncTaskInvoker());
processEngineConfiguration.getDelegateInterceptor().handleInvocation(invocation);
Object invocationResult = invocation.getInvocationResult();
if (invocationResult instanceof CompletableFuture) {
CompletableFuture<Object> future = (CompletableFuture<Object>) invocationResult;
CommandContextUtil.getAgenda(commandContext).planFutureOperation(future, new FutureJavaDelegateCompleteAction(futureJavaDelegate, execution, loggingSessionEnabled));
} else {
throw new FlowableIllegalStateException(
"Invocation result " + invocationResult + " from invocation " + invocation + " was not a CompletableFuture");
}
} else {
throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did neither resolve to an implementation of " + ActivityBehavior.class + " nor " + JavaDelegate.class);
}
} else {
if (loggingSessionEnabled) {
BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SKIP_TASK, "Skipped service task " + execution.getCurrentActivityId() +
" with skip expression " + skipExpressionText, execution);
}
leave(execution);
}
} catch (Exception exc) {
handleException(exc, execution, loggingSessionEnabled);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Ensure the FutureJavaDelegate's invocation returns a non-null CompletableFuture (use CompletableFuture.completedFuture(value) for immediate results)
- Check for Flowable version upgrade breakage: FutureJavaDelegate contract now requires CompletableFuture results
- Audit the delegate class for early-return paths that skip building the future
- If plain synchronous semantics are desired, implement JavaDelegate instead of FutureJavaDelegate
Example fix
// before
public Object execute(DelegateExecution execution) {
if (input == null) return null; // breaks contract
return doWork(input);
}
// after
public CompletableFuture<Object> execute(DelegateExecution execution) {
if (input == null) return CompletableFuture.completedFuture(null);
return CompletableFuture.supplyAsync(() -> doWork(input));
} Defensive patterns
Strategy: type-guard
Validate before calling
// Assert delegate result type in tests
Object result = delegate.execute(execution);
if (!(result instanceof CompletableFuture)) {
throw new IllegalStateException("FutureJavaDelegate must return CompletableFuture");
} Type guard
public static boolean isFutureResult(Object o) {
return o instanceof CompletableFuture;
} Try / catch
try {
processEngine.getRuntimeService().signal(executionId);
} catch (FlowableIllegalStateException e) {
if (e.getMessage().contains("was not a CompletableFuture")) {
// fix FutureJavaDelegate to always return a CompletableFuture
}
} Prevention
- Use CompletableFuture.completedFuture for synchronous results
- Never return null from FutureJavaDelegate invoke paths
- Re-audit FutureJavaDelegate implementations after Flowable upgrades
When it happens
Trigger: A delegate expression resolving to a FutureJavaDelegate implementation whose invoke/execution method returns null or a non-CompletableFuture object; the check `invocationResult instanceof CompletableFuture` fails and this error is raised.
Common situations: Mixing Flowable versions where FutureJavaDelegate semantics changed (newer versions expect CompletableFuture returns); a FutureJavaDelegate implemented against an older interface that returned plain values; returning null on early-exit code paths.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Invocation result + invocationResult + from invocation + inv
- Invalid usage of async_activate job handler, variable scope
- Future was interrupted
- None of the available futures completed within the max timeo
- No processDefinitionId, processDefinitionKey provided
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/b832c30c13405ee3.
Report an issue: GitHub.