flowable/flowable-engine · error · ActivitiException

${exc.getMessage()}

Error message

${exc.getMessage()}

What it means

In ServiceTaskDelegateExpressionActivityBehavior.execute, when the delegate throws an exception that is not a BpmnError (so no error can be propagated), the engine re-throws it as an ActivitiException whose message is the original exception's message, keeping the original as the cause. It is a generic re-wrap of delegate failures.

Solutions

  1. Read the 'Caused by' chain for the real exception thrown by the delegate and fix it there
  2. Throw BpmnError from the delegate for business errors so ErrorPropagation can route to error boundary events
  3. Add null-safe message handling/logging in the delegate to avoid 'null' messages
  4. Wrap risky delegate logic (network, parsing) with explicit checks before throwing

Example fix

// before (delegate)
public void execute(DelegateExecution e) {
    callRemote((String) e.getVariable("url")); // NPE if url is null
}
// after
public void execute(DelegateExecution e) {
    String url = (String) e.getVariable("url");
    if (url == null) {
        throw new BpmnError("MISSING_URL", "process variable 'url' is required");
    }
    callRemote(url);
}
Defensive patterns

Strategy: try-catch

Validate before calling

String url = (String) execution.getVariable("url");
if (url == null || url.isEmpty()) {
    throw new BpmnError("MISSING_URL");
}

Try / catch

try {
    taskService.complete(taskId);
} catch (org.activiti.engine.ActivitiException e) {
    Throwable c = e.getCause();
    // inspect root cause; if business error, route to error handling
}

Prevention

When it happens

Trigger: A delegate resolved via delegateExpression throws a non-BpmnError exception whose walk-back through getCause() finds no BpmnError; e.g. a JavaDelegate's execute() throws RuntimeException, IOException, or NPE during task execution.

Common situations: Delegates failing on null process variables, network/DB errors inside the delegate, class-cast issues when reading variables; message may be null when the underlying exception has no message, making logs confusing.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java:133

            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)