flowable/flowable-engine · error · ActivitiException

Exception while invoking TaskListener

Error message

Exception while invoking TaskListener: ${e.getMessage()}

What it means

Thrown as ActivitiException from DelegateExpressionTaskListener.notify when the resolved delegate is a TaskListener but invoking it through the configured DelegateInterceptor (TaskListenerInvocation) throws an exception. The original exception's message is embedded and the cause is attached.

Solutions

  1. Read the wrapped cause (e.getCause()) — the message here only repeats the inner exception's message.
  2. Fix the exception inside the TaskListener implementation itself (null checks, error handling around external calls).
  3. Check the delegate interceptor configuration for custom wrapping that might throw.
  4. Add logging inside the listener to pinpoint the failing statement.
  5. If the failure is environmental (DB, network), make the listener retry or degrade gracefully instead of throwing.

Example fix

// before: listener body can NPE
public void notify(DelegateTask task) { task.getVariable("cfg").toString(); }

// after: guard
public void notify(DelegateTask task) {
  Object cfg = task.getVariable("cfg");
  if (cfg != null) { cfg.toString(); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: resolve and dry-run the delegate where possible
Object delegate = applicationContext.getBean("myTaskListener");
if (!(delegate instanceof TaskListener)) {
    throw new IllegalStateException("myTaskListener is not a TaskListener");
}

Try / catch

try {
    taskService.complete(taskId);
} catch (ActivitiException e) {
    if (e.getMessage().startsWith("Exception while invoking TaskListener")) {
        Throwable root = e;
        while (root.getCause() != null) root = root.getCause();
        logger.error("TaskListener failed: ", root);
    }
    throw e;
}

Prevention

When it happens

Trigger: A <activiti:taskListener event="..." delegateExpression="${bean}"/> resolves to a valid TaskListener, but during handleInvocation the listener's notify(DelegateTask) throws — e.g. NPE inside the listener, failed dependency injection, DB/service call failure inside the listener body.

Common situations: Listener code assuming injected fields/variables are non-null; listener calling an external service that fails; exceptions in Spring bean initialization during expression resolution; delegate interceptor wrapping adding behavior (transactions, security) that throws.

Related errors


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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/listener/DelegateExpressionTaskListener.java:53

    public DelegateExpressionTaskListener(Expression expression, List<FieldDeclaration> fieldDeclarations) {
        this.expression = expression;
        this.fieldDeclarations = fieldDeclarations;
    }

    @Override
    public void notify(DelegateTask delegateTask) {
        // Note: we can't cache the result of the expression, because the
        // execution can change: eg. delegateExpression='${mySpringBeanFactory.randomSpringBean()}'
        Object delegate = expression.getValue(delegateTask);
        ClassDelegate.applyFieldDeclaration(fieldDeclarations, delegate);

        if (delegate instanceof TaskListener) {
            try {
                Context.getProcessEngineConfiguration()
                        .getDelegateInterceptor()
                        .handleInvocation(new TaskListenerInvocation((TaskListener) delegate, delegateTask));
            } catch (Exception e) {
                throw new ActivitiException("Exception while invoking TaskListener: " + e.getMessage(), e);
            }
        } else {
            throw new ActivitiIllegalArgumentException("Delegate expression " + expression
                    + " did not resolve to an implementation of " + TaskListener.class);
        }
    }

    /**
     * returns the expression text for this task listener. Comes in handy if you want to check which listeners you already have.
     */
    public String getExpressionText() {
        return expression.getExpressionText();
    }

}

View on GitHub (pinned to d6d39ce1c6)