flowable/flowable-engine · error · ActivitiException
${exc.getMessage()}
Error message
${exc.getMessage()} What it means
In ServiceTaskExpressionActivityBehavior.execute, when the expression-invoked method throws a non-BpmnError exception, the engine propagates the error if a BpmnError is found in the cause chain, otherwise re-throws as ActivitiException with the original exception's message and cause. It signals the flowable:expression call failed at runtime.
Solutions
- Fix the exception in the bean method invoked by the expression — inspect the 'Caused by' chain
- Guard against missing/null process variables inside the bean before using them
- Throw BpmnError from the bean for business errors so error boundary events can catch them
- Return/validate early: check inputs in the bean method and fail with a clear message
Example fix
// before
public void handle(DelegateExecution e) {
process((Order) e.getVariable("order")); // NPE when 'order' unset
}
// after
public void handle(DelegateExecution e) {
Order order = (Order) e.getVariable("order");
if (order == null) {
throw new BpmnError("NO_ORDER", "process variable 'order' is required");
}
process(order);
} Defensive patterns
Strategy: try-catch
Validate before calling
Object order = execution.getVariable("order");
if (!(order instanceof Order)) {
throw new BpmnError("NO_ORDER");
} Type guard
public static boolean hasVariable(DelegateExecution e, String name, Class<?> type) {
return type.isInstance(e.getVariable(name));
} Try / catch
try {
taskService.complete(taskId);
} catch (org.activiti.engine.ActivitiException e) {
if (e.getMessage() == null) {
log.error("expression failed, cause chain:", e); // message mirrors cause's message
}
throw e;
} Prevention
- Guard bean methods invoked via flowable:expression against null/missing variables
- Always pass DelegateExecution explicitly in expression method signatures and read variables safely
- Throw BpmnError for business errors to enable error boundary event handling
- Keep exception messages non-null and descriptive since the engine copies them
When it happens
Trigger: flowable:expression="${bean.method(exec)}" where the invoked method throws (NPE, IllegalArgumentException, IO failure) and no BpmnError exists anywhere in the cause chain.
Common situations: Expression calls a Spring bean method that dereferences a missing process variable; the bean throws a checked exception wrapped in RuntimeException; the method signature changed and evaluation fails; message is null in logs because the original exception had no message.
Related errors
- ${exc.getMessage()}
- Category expression does not resolve to a string
- ' didn't resolve to a Collection
- completionCondition ' ' does not evaluate to a boolean value
- Could not execute inner activity behavior of multi instance…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/f4dda73e892c0a2b.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/ServiceTaskExpressionActivityBehavior.java:106
Throwable cause = exc;
BpmnError error = null;
while (cause != null) {
if (cause instanceof BpmnError) {
error = (BpmnError) cause;
break;
} else if (cause instanceof RuntimeException) {
if (ErrorPropagation.mapException((RuntimeException) cause, activityExecution, mapExceptions)) {
return;
}
}
cause = cause.getCause();
}
if (error != null) {
ErrorPropagation.propagateError(error, activityExecution);
} else {
throw new ActivitiException(exc.getMessage(), exc);
}
}
}
}
View on GitHub (pinned to d6d39ce1c6)