flowable/flowable-engine · error · ActivitiException
Exception while invoking TaskListener:
Error message
Exception while invoking TaskListener:
What it means
Wraps any Exception raised while invoking a registered TaskListener during task events (create/complete/assignment etc.). The engine routes the listener through the configured DelegateInterceptor; if the listener's notify() throws, it is rethrown as an ActivitiException with the delegate's message. The original exception is kept as the cause, so the real failure is in your listener code.
Solutions
- Inspect the cause (getCause()) of this exception — the real bug is inside your TaskListener's notify() method
- Fix or add error handling/logging in the listener implementation before it rethrows
- Verify any delegateExpression beans exist and are injectable in the current context
- Test the listener logic in isolation with a unit test calling notify(DelegateTask)
Example fix
// before: listener that throws
public void notify(DelegateTask task) {
String val = (String) task.getVariable("missing"); // NPE risk
doWork(val.toLowerCase());
}
// after: defensive listener
public void notify(DelegateTask task) {
Object val = task.getVariable("missing");
if (val == null) { LOG.warn("variable missing, skipping"); return; }
doWork(val.toString().toLowerCase());
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify listener bean/expr resolves before deploy Object bean = applicationContext.getBean(listenerBeanName); // throws early if missing
Try / catch
try {
taskService.complete(taskId);
} catch (ActivitiException e) {
Throwable root = e; while (root.getCause() != null) root = root.getCause();
LOG.error("TaskListener failed: " + root.getMessage(), root);
} Prevention
- Write defensive listeners: null-check variables, never rethrow raw exceptions
- Unit-test every TaskListener's notify() in isolation
- Log and swallow non-fatal listener errors inside the listener itself
- Verify delegateExpression beans exist for every deployment environment
When it happens
Trigger: A custom TaskListener (class, expression, or delegateExpression) throws any exception during fireEvent(TaskListener.EVENTNAME_*) — typically on task creation, completion, or assignment inside TaskEntity.
Common situations: NullPointerException inside listener code; listener delegateExpression points to a bean that fails to resolve; listener calls engine APIs that themselves fail; classpath issues loading the listener class; the DelegateInterceptor throws (e.g. custom security interceptor).
Related errors
- A delegated task cannot be completed, but should be…
- A delegated cannot be completed, but should be resolved…
- Builder is missing constructor (can't pass features)
- callback id is null
- Cannot access id method/field for JPA Entity
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/325058e19e933a92.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/TaskEntity.java:777
this.formKey = formKey;
}
public void fireEvent(String taskEventName) {
TaskDefinition taskDefinition = getTaskDefinition();
if (taskDefinition != null) {
List<TaskListener> taskEventListeners = getTaskDefinition().getTaskListener(taskEventName);
if (taskEventListeners != null) {
for (TaskListener taskListener : taskEventListeners) {
ExecutionEntity execution = getExecution();
if (execution != null) {
setEventName(taskEventName);
}
try {
Context.getProcessEngineConfiguration()
.getDelegateInterceptor()
.handleInvocation(new TaskListenerInvocation(taskListener, (DelegateTask) this));
} catch (Exception e) {
throw new ActivitiException("Exception while invoking TaskListener: " + e.getMessage(), e);
}
}
}
}
}
@Override
protected boolean isActivityIdUsedForDetails() {
return false;
}
// Override from VariableScopeImpl
// Overridden to avoid fetching *all* variables (as is the case in the super call)
@Override
protected VariableInstanceEntity getSpecificVariable(String variableName) {
CommandContext commandContext = Context.getCommandContext();
if (commandContext == null) {View on GitHub (pinned to d6d39ce1c6)