flowable/flowable-engine · error · FlowableIllegalArgumentException

<delegateClassName> doesn't implement JavaDelegate, FutureJa

Error message

<delegateClassName> doesn't implement JavaDelegate, FutureJavaDelegate nor ActivityBehavior

What it means

ClassDelegate.getActivityBehaviorInstance() instantiates the delegate class and only accepts instances of ActivityBehavior, JavaDelegate, or FutureJavaDelegate, wrapping JavaDelegates in service-task behaviors. Anything else yields a FlowableIllegalArgumentException stating the class implements neither JavaDelegate, FutureJavaDelegate nor ActivityBehavior. This is the central contract check for class-based service task delegates.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:283

        if (activityBehaviorInstance instanceof SubProcessActivityBehavior) {
            ((SubProcessActivityBehavior) activityBehaviorInstance).completed(execution);
        } else {
            throw new FlowableException("completed() can only be called on a " + SubProcessActivityBehavior.class.getName() + " instance for " + execution);
        }
    }

    protected ActivityBehavior getActivityBehaviorInstance() {
        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);

        if (delegateInstance instanceof ActivityBehavior) {
            return determineBehaviour((ActivityBehavior) delegateInstance);
        } else if (delegateInstance instanceof JavaDelegate) {
            return determineBehaviour(new ServiceTaskJavaDelegateActivityBehavior((JavaDelegate) delegateInstance, triggerable, skipExpression));
        } else if (delegateInstance instanceof FutureJavaDelegate) {
            return determineBehaviour(new ServiceTaskFutureJavaDelegateActivityBehavior((FutureJavaDelegate<?>) delegateInstance, triggerable, skipExpression, mapExceptions));
        } else {
            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + JavaDelegate.class.getName() + ", " + FutureJavaDelegate.class.getName() + " nor " + ActivityBehavior.class.getName());
        }
    }

    // Adds properties to the given delegation instance (eg multi instance) if needed
    protected ActivityBehavior determineBehaviour(ActivityBehavior delegateInstance) {
        if (hasMultiInstanceCharacteristics()) {
            multiInstanceActivityBehavior.setInnerActivityBehavior((AbstractBpmnActivityBehavior) delegateInstance);
            return multiInstanceActivityBehavior;
        }
        return delegateInstance;
    }

}

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the class implement org.flowable.engine.delegate.JavaDelegate (execute(DelegateExecution)) — the most common fix for service tasks.
  2. Alternatively implement FutureJavaDelegate or extend a Flowable ActivityBehavior for custom control flow.
  3. Verify the class attribute and imports reference Flowable types, not Activiti ones.
  4. Rebuild and redeploy so the engine loads the corrected class.

Example fix

// before
public class MyTask { public void run() { ... } }
<!-- flowable:class="com.acme.MyTask" -->
// after
public class MyTask implements JavaDelegate {
    @Override public void execute(DelegateExecution execution) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = Class.forName(serviceTaskClass);
boolean ok = ActivityBehavior.class.isAssignableFrom(c)
        || JavaDelegate.class.isAssignableFrom(c)
        || FutureJavaDelegate.class.isAssignableFrom(c);
if (!ok) throw new IllegalStateException("Service task class must implement JavaDelegate/FutureJavaDelegate/ActivityBehavior");

Type guard

boolean isValidDelegate(Object o) {
    return o instanceof ActivityBehavior || o instanceof JavaDelegate || o instanceof FutureJavaDelegate;
}

Try / catch

try {
    runtimeService.startProcessInstanceByKey("proc");
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("JavaDelegate")) { /* fix service task class */ }
    throw e;
}

Prevention

When it happens

Trigger: A service task (or other behavior-backed element) declares flowable:class='<delegateClassName>' and the class implements none of ActivityBehavior, JavaDelegate (execute(DelegateExecution)), or FutureJavaDelegate; also fired by execute/trigger/completing/completed when the behavior instance is first resolved.

Common situations: Pointing flowable:class at an arbitrary helper bean; missing import so the class implements org.activiti.engine.impl.pvm.delegate.ActivityBehavior or wrong package; the class implements only a listener interface; wrong fully qualified name resolving to an unrelated class.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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