{"record":{"id":"c43791bb8076fc68","repo":"flowable/flowable-engine","slug":"delegateclassname-doesn-t-implement-customproper","errorCode":null,"errorMessage":"<delegateClassName> doesn't implement CustomPropertiesResolver","messagePattern":"<delegateClassName> doesn't implement CustomPropertiesResolver","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java","lineNumber":156,"sourceCode":"    protected ExecutionListener getExecutionListenerInstance() {\n        return new DelegateExecutionListener(instantiateDelegate(className, fieldDeclarations));\n    }\n\n    protected TransactionDependentExecutionListener getTransactionDependentExecutionListenerInstance() {\n        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);\n        if (delegateInstance instanceof TransactionDependentExecutionListener) {\n            return (TransactionDependentExecutionListener) delegateInstance;\n        } else {\n            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + \" doesn't implement \" + TransactionDependentExecutionListener.class);\n        }\n    }\n\n    protected CustomPropertiesResolver getCustomPropertiesResolverInstance() {\n        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);\n        if (delegateInstance instanceof CustomPropertiesResolver) {\n            return (CustomPropertiesResolver) delegateInstance;\n        } else {\n            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + \" doesn't implement \" + CustomPropertiesResolver.class);\n        }\n    }\n\n    protected TaskListener getTaskListenerInstance() {\n        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);\n        if (delegateInstance instanceof TaskListener) {\n            return (TaskListener) delegateInstance;\n        } else {\n            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + \" doesn't implement \" + TaskListener.class);\n        }\n    }\n\n    protected TransactionDependentTaskListener getTransactionDependentTaskListenerInstance() {\n        Object delegateInstance = instantiateDelegate(className, fieldDeclarations);\n        if (delegateInstance instanceof TransactionDependentTaskListener) {\n            return (TransactionDependentTaskListener) delegateInstance;\n        } else {\n            throw new FlowableIllegalArgumentException(delegateInstance.getClass().getName() + \" doesn't implement \" + TransactionDependentTaskListener.class);","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java#L138-L174","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic class MyResolver { public Map<String,Object> getProps() { ... } }\n// after\npublic class MyResolver implements CustomPropertiesResolver {\n    @Override\n    public Map<String, Object> getCustomPropertiesMap(DelegateExecution execution) { ... }\n}","handlingStrategy":"validation","validationCode":"Class<?> c = Class.forName(cfg.getCustomPropertiesResolverClass());\nif (!CustomPropertiesResolver.class.isAssignableFrom(c))\n    throw new IllegalStateException(\"Class must implement CustomPropertiesResolver\");","typeGuard":"boolean isValidResolver(Object o) { return o instanceof CustomPropertiesResolver; }","tryCatchPattern":"try {\n    processEngine.getRuntimeService().startProcessInstanceByKey(\"proc\");\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"CustomPropertiesResolver\")) { /* fix class */ }\n    throw e;\n}","preventionTips":["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."],"tags":["flowable","delegate","listener","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}