flowable/flowable-engine · error · FlowableIllegalArgumentException

Delegate expression

Error message

Delegate expression 

What it means

The plan item's delegateExpression resolved to an object implementing none of the four supported interfaces: PlanItemActivityBehavior, CmmnActivityBehavior, PlanItemJavaDelegate, or PlanItemFutureJavaDelegate. Flowable does not know how to execute the object, so it throws FlowableIllegalArgumentException listing the accepted interfaces.

Solutions

  1. Make the delegate class implement org.flowable.cmmn.api.delegate.PlanItemJavaDelegate (or PlanItemFutureJavaDelegate for async work).
  2. If the class should control plan item lifecycle, implement PlanItemActivityBehavior; for generic behavior, CmmnActivityBehavior.
  3. Verify the expression resolves to the intended bean (check the delegateExpression value and Spring context).
  4. Catch FlowableIllegalArgumentException and log the resolved object's getClass() plus implemented interfaces to see what is missing.

Example fix

// before
public class MyWorker implements org.flowable.engine.delegate.JavaDelegate { ... }
// after
public class MyWorker implements org.flowable.cmmn.api.delegate.PlanItemJavaDelegate {
    public Object execute(DelegatePlanItemInstance planItemInstance) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = applicationContext.getBean(beanName);
boolean ok = delegate instanceof PlanItemActivityBehavior || delegate instanceof CmmnActivityBehavior
    || delegate instanceof PlanItemJavaDelegate || delegate instanceof PlanItemFutureJavaDelegate;
if (!ok) throw new IllegalArgumentException(beanName + " implements none of the supported CMMN delegate interfaces");

Type guard

boolean isValidCmmnDelegate(Object d) {
    return d instanceof PlanItemActivityBehavior || d instanceof CmmnActivityBehavior
        || d instanceof PlanItemJavaDelegate || d instanceof PlanItemFutureJavaDelegate;
}

Try / catch

try {
    // start case / execute plan item
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().startsWith("Delegate expression")) {
        log.error("Delegate for expression resolved to unsupported type: {}", e.getMessage());
    }
}

Prevention

When it happens

Trigger: execute() of PlanItemDelegateExpressionActivityBehavior, when DelegateExpressionUtil.resolveDelegateExpression returns an object whose class implements none of the four supported interfaces.

Common situations: Implementing the plain Flowable (BPMN) JavaDelegate interface instead of PlanItemJavaDelegate in a CMMN case; referencing a bean that is a helper/service class, not a delegate; refactoring renamed the interface the class implements; Spring bean wired to the wrong component.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/PlanItemDelegateExpressionActivityBehavior.java:78

        } else if (delegate instanceof CmmnActivityBehavior) {
            try {
                ((CmmnActivityBehavior) delegate).execute(planItemInstanceEntity);
            } catch (BusinessError fault) {
                FaultPropagation.propagateFault(fault, commandContext, planItemInstanceEntity);
            }

        } else if (delegate instanceof PlanItemJavaDelegate) {
            // PlanItemJavaDelegateActivityBehavior already catches CmmnFault internally
            PlanItemJavaDelegateActivityBehavior behavior = new PlanItemJavaDelegateActivityBehavior((PlanItemJavaDelegate) delegate);
            behavior.execute(planItemInstanceEntity);

        } else if (delegate instanceof PlanItemFutureJavaDelegate) {
            // PlanItemFutureJavaDelegateActivityBehavior already catches CmmnFault internally
            PlanItemFutureJavaDelegateActivityBehavior behavior = new PlanItemFutureJavaDelegateActivityBehavior((PlanItemFutureJavaDelegate<?>) delegate);
            behavior.execute(planItemInstanceEntity);
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did neither resolve to an implementation of " +
                    PlanItemActivityBehavior.class + ", " + CmmnActivityBehavior.class + ", " + PlanItemJavaDelegate.class + " nor "
                    + PlanItemFutureJavaDelegate.class);
        }
    }

    @Override
    public void trigger(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
        Expression expressionObject = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getExpressionManager().createExpression(expression);
        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expressionObject, planItemInstanceEntity, fieldExtensions);
        if (delegate instanceof CmmnTriggerableActivityBehavior) { // includes CmmnTriggerableActivityBehavior
            ((CmmnTriggerableActivityBehavior) delegate).trigger(planItemInstanceEntity);

        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did neither resolve to an implementation of "
                    + CmmnTriggerableActivityBehavior.class);
        }
    }

View on GitHub (pinned to d6d39ce1c6)