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

  1. Fix the exception in the bean method invoked by the expression — inspect the 'Caused by' chain
  2. Guard against missing/null process variables inside the bean before using them
  3. Throw BpmnError from the bean for business errors so error boundary events can catch them
  4. 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

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


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)