flowable/flowable-engine · error · FlowableIllegalArgumentException
<delegateClassName> doesn't implement CustomPropertiesResolv
Error message
<delegateClassName> doesn't implement CustomPropertiesResolver
What it means
ClassDelegate instantiates the class configured as a custom properties resolver and casts it to CustomPropertiesResolver. When the instantiated object does not implement that interface, a FlowableIllegalArgumentException naming the class and the expected interface is thrown. Flowable requires this interface to resolve the custom properties passed to transaction-dependent listeners.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java:156
protected ExecutionListener getExecutionListenerInstance() {
return new DelegateExecutionListener(instantiateDelegate(className, fieldDeclarations));
}
protected TransactionDependentExecutionListener getTransactionDependentExecutionListenerInstance() {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof TransactionDependentExecutionListener) {
return (TransactionDependentExecutionListener) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TransactionDependentExecutionListener.class);
}
}
protected CustomPropertiesResolver getCustomPropertiesResolverInstance() {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof CustomPropertiesResolver) {
return (CustomPropertiesResolver) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + CustomPropertiesResolver.class);
}
}
protected TaskListener getTaskListenerInstance() {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof TaskListener) {
return (TaskListener) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TaskListener.class);
}
}
protected TransactionDependentTaskListener getTransactionDependentTaskListenerInstance() {
Object delegateInstance = instantiateDelegate(className, fieldDeclarations);
if (delegateInstance instanceof TransactionDependentTaskListener) {
return (TransactionDependentTaskListener) delegateInstance;
} else {
throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + " doesn't implement " + TransactionDependentTaskListener.class);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the configured class implement org.flowable.engine.delegate.CustomPropertiesResolver (getCustomPropertiesMap method).
- Check the customPropertiesResolver class attribute points to the intended fully qualified class name.
- Redeploy so the runtime classpath contains the interface implementation.
- If no custom properties are needed, remove the customPropertiesResolver attribute entirely.
Example fix
// before
public class MyResolver { public Map<String,Object> getProps() { ... } }
// after
public class MyResolver implements CustomPropertiesResolver {
@Override
public Map<String, Object> getCustomPropertiesMap(DelegateExecution execution) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(cfg.getCustomPropertiesResolverClass());
if (!CustomPropertiesResolver.class.isAssignableFrom(c))
throw new IllegalStateException("Class must implement CustomPropertiesResolver"); Type guard
boolean isValidResolver(Object o) { return o instanceof CustomPropertiesResolver; } Try / catch
try {
processEngine.getRuntimeService().startProcessInstanceByKey("proc");
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("CustomPropertiesResolver")) { /* fix class */ }
throw e;
} Prevention
- Only pass classes implementing CustomPropertiesResolver to customPropertiesResolver attributes.
- Remove the attribute when no custom properties are needed.
- Add a deployment-time validation test for all resolvers.
When it happens
Trigger: A BPMN element declares flowable:customPropertiesResolver class='<delegateClassName>' and the class does not implement org.flowable.engine.delegate.CustomPropertiesResolver; getCustomPropertiesMap is called when the transaction-dependent listener fires.
Common situations: Reusing a random bean as customPropertiesResolver; typos in the class attribute pointing at another class; the class implements the listener interface but not the resolver interface; stale deployment artifacts.
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
- <delegateClassName> doesn't implement TransactionDependentEx
- <delegateClassName> doesn't implement TaskListener
- <delegateClassName> doesn't implement TransactionDependentTa
- <delegateClassName> doesn't implement JavaDelegate, FutureJa
- <delegateClassName> doesn't implement FlowableCollectionHand
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c43791bb8076fc68.
Report an issue: GitHub.