Java delegate expression is not getting resolved

Hi everyone,

I am getting error while running the camunda engine where it says -

org.camunda.bpm.engine.ProcessEngineException: Unknown property used in expression: ${userValidationDelegate}. Cause: Cannot resolve identifier ‘userValidationDelegate’

Below is my service task where I am reffering my spring bean -

<bpmn:serviceTask id=“Task_CheckUser” name=“Check if User is valid” camunda:asyncBefore=“true” camunda:delegateExpression=“${userValidationDelegate}”>
bpmn:extensionElements
camunda:failedJobRetryTimeCycleR3/PT5M</camunda:failedJobRetryTimeCycle>
</bpmn:extensionElements>
bpmn:incomingFlow_To_UserCheck</bpmn:incoming>
bpmn:outgoingFlow_To_IsUserValid</bpmn:outgoing>
</bpmn:serviceTask>

Below is my java class -

@Slf4j
@Component(“userValidationDelegate”)
public class UserValidationDelegate extends JavaDelegate {

@Value("${configuration.userConfirmationUrl}")
private String userConfirmationUrl;

@Override
public void execute(DelegateExecution execution) throws Exception {
    //logic is implemented here with an api call

}

I have kept my spring beans and camunda engine in same appplication. So the deployment is happening as a single application. Also below is my configuration in application.yaml file

camunda:
bpm:
database:
schema-name: camunda
table-prefix: camunda.
schema-update: true
history-level: FULL
admin-user:
id: ${COCKPIT_USER}
password: ${COCKPIT_PASSWORD}
authorization:
enabled: true
job-execution:
deployment-aware: true
webapp:
index-redirect-enabled: true

I am using spring boot 3.3.8 with camunda 7.22 version. I would higly appreciate if anybody can help me here!

@Aman_28 Can you share the full stack trace?

JavaDelegate is an interface, so you should write as

@Slf4j
@Component
public class UserValidationDelegate implements JavaDelegate {
       
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        // delegation code here
    }
}

Refer these examples:

Thank you for looking into this!
Sorry for the confusion. I am implementing JavaDelegate in my Delegate class

@Slf4j
@Component(“userValidationDelegate”)
public class UserValidationDelegate implements JavaDelegate {

@Value("${configuration.userConfirmationUrl}")
private String userConfirmationUrl;

@Override
public void execute(DelegateExecution execution) throws Exception {
    //logic is implemented here with an api call
}

This is how I have configured in bpm diagram -

PFB full stack trace :

org.camunda.bpm.engine.ProcessEngineException: Unknown property used in expression: ${userValidationDelegate}. Cause: Cannot resolve identifier 'userValidationDelegate'
	at org.camunda.bpm.engine.impl.el.JuelExpression.getValue(JuelExpression.java:63)
	at org.camunda.bpm.engine.impl.el.JuelExpression.getValue(JuelExpression.java:51)
	at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskDelegateExpressionActivityBehavior$3.call(ServiceTaskDelegateExpressionActivityBehavior.java:107)
	at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskDelegateExpressionActivityBehavior$3.call(ServiceTaskDelegateExpressionActivityBehavior.java:102)
	at org.camunda.bpm.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior.executeWithErrorPropagation(AbstractBpmnActivityBehavior.java:90)
	at org.camunda.bpm.engine.impl.bpmn.behavior.ServiceTaskDelegateExpressionActivityBehavior.performExecution(ServiceTaskDelegateExpressionActivityBehavior.java:127)
	at org.camunda.bpm.engine.impl.bpmn.behavior.TaskActivityBehavior.execute(TaskActivityBehavior.java:69)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute$2.callback(PvmAtomicOperationActivityExecute.java:61)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute$2.callback(PvmAtomicOperationActivityExecute.java:50)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.continueIfExecutionDoesNotAffectNextOperation(PvmExecutionImpl.java:2109)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute.execute(PvmAtomicOperationActivityExecute.java:42)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationActivityExecute.execute(PvmAtomicOperationActivityExecute.java:31)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:121)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:627)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:601)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl$6.callback(PvmExecutionImpl.java:2048)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl$6.callback(PvmExecutionImpl.java:2045)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.continueExecutionIfNotCanceled(PvmExecutionImpl.java:2115)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.dispatchDelayedEventsAndPerformOperation(PvmExecutionImpl.java:2064)
	at org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl.dispatchDelayedEventsAndPerformOperation(PvmExecutionImpl.java:2045)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(PvmAtomicOperationTransitionNotifyListenerStart.java:61)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(PvmAtomicOperationTransitionNotifyListenerStart.java:30)
	at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:70)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:121)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:636)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:611)
	at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:66)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:121)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:636)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:611)
	at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:66)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:121)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:636)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:611)
	at org.camunda.bpm.engine.impl.core.operation.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:66)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:121)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:627)
	at org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:601)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationTransitionCreateScope.scopeCreated(PvmAtomicOperationTransitionCreateScope.java:38)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationCreateScope.execute(PvmAtomicOperationCreateScope.java:53)
	at org.camunda.bpm.engine.impl.pvm.runtime.operation.PvmAtomicOperationCreateScope.execute(PvmAtomicOperationCreateScope.java:27)
	at org.camunda.bpm.engine.impl.interceptor.AtomicOperationInvocation.execute(AtomicOperationInvocation.java:99)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.invokeNext(CommandInvocationContext.java:141)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performNext(CommandInvocationContext.java:128)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:96)
	at org.camunda.bpm.engine.impl.interceptor.CommandInvocationContext.performOperation(CommandInvocationContext.java:86)
	at org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler.execute(AsyncContinuationJobHandler.java:81)
	at org.camunda.bpm.engine.impl.jobexecutor.AsyncContinuationJobHandler.execute(AsyncContinuationJobHandler.java:40)
	at org.camunda.bpm.engine.impl.persistence.entity.JobEntity.execute(JobEntity.java:135)
	at org.camunda.bpm.engine.impl.cmd.ExecuteJobsCmd.execute(ExecuteJobsCmd.java:110)
	at org.camunda.bpm.engine.impl.cmd.ExecuteJobsCmd.execute(ExecuteJobsCmd.java:43)
	at org.camunda.bpm.engine.impl.interceptor.CommandExecutorImpl.execute(CommandExecutorImpl.java:28)
	at org.camunda.bpm.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:111)
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.lambda$execute$0(SpringTransactionInterceptor.java:62)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
	at org.camunda.bpm.engine.spring.SpringTransactionInterceptor.execute(SpringTransactionInterceptor.java:62)
	at org.camunda.bpm.engine.impl.interceptor.ProcessApplicationContextInterceptor.execute(ProcessApplicationContextInterceptor.java:70)
	at org.camunda.bpm.engine.impl.interceptor.CommandCounterInterceptor.execute(CommandCounterInterceptor.java:34)
	at org.camunda.bpm.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:33)
	at org.camunda.bpm.engine.impl.interceptor.ExceptionCodeInterceptor.execute(ExceptionCodeInterceptor.java:55)
	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobHelper.executeJob(ExecuteJobHelper.java:57)
	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.executeJob(ExecuteJobsRunnable.java:110)
	at org.camunda.bpm.engine.impl.jobexecutor.ExecuteJobsRunnable.run(ExecuteJobsRunnable.java:71)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: org.camunda.bpm.impl.juel.jakarta.el.PropertyNotFoundException: Cannot resolve identifier 'userValidationDelegate'
	at org.camunda.bpm.impl.juel.AstIdentifier.eval(AstIdentifier.java:83)
	at org.camunda.bpm.impl.juel.AstEval.eval(AstEval.java:50)
	at org.camunda.bpm.impl.juel.AstNode.getValue(AstNode.java:26)
	at org.camunda.bpm.impl.juel.TreeValueExpression.getValue(TreeValueExpression.java:112)
	at org.camunda.bpm.engine.impl.delegate.ExpressionGetInvocation.invoke(ExpressionGetInvocation.java:40)
	at org.camunda.bpm.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:58)
	at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocationInContext(DefaultDelegateInterceptor.java:92)
	at org.camunda.bpm.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:63)
	at org.camunda.bpm.engine.impl.el.JuelExpression.getValue(JuelExpression.java:60)
	... 83 more

Could somebody please assist with this? It’s becoming a blocker for us now.

The error “Unknown property used in expression: ${userValidationDelegate}. Cause: Cannot resolve identifier ‘userValidationDelegate’” indicates that Camunda cannot find your Spring bean when trying to resolve the delegate expression. Here’s a comprehensive solution to resolve this issue.

Root Cause Analysis

The problem occurs when Camunda’s process engine cannot access the Spring application context where your bean is registered. This typically happens due to context switching issues between the Camunda engine and your Spring Boot application.

Solution Steps

1. Verify Your Java Delegate Implementation

Ensure your delegate class is properly implemented:

@Slf4j
@Component("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
    
    @Value("${configuration.userConfirmationUrl}")
    private String userConfirmationUrl;
    
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        // Your business logic here
        log.info("Executing user validation delegate");
        // Process your logic and set variables if needed
        // execution.setVariable("result", someValue);
    }
}

Key points:

  • The class must implement org.camunda.bpm.engine.delegate.JavaDelegate
  • Use @Component with the exact name you reference in your BPMN
  • Keep the delegate stateless and avoid storing data in fields

2. Configure Camunda Spring Boot Integration

Add the following configuration to ensure proper Spring context integration:

camunda:
  bpm:
    process-engine-name: default
    application:
      enabled: true
    database:
      schema-name: camunda
      table-prefix: camunda.
      schema-update: true
    history-level: FULL
    admin-user:
      id: ${COCKPIT_USER}
      password: ${COCKPIT_PASSWORD}
    authorization:
      enabled: true
    job-execution:
      deployment-aware: true
    webapp:
      index-redirect-enabled: true

3. Create a Process Application Class

Create a @ProcessApplication annotated class to ensure proper context registration:

@SpringBootApplication
@EnableProcessApplication
public class CamundaApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(CamundaApplication.class, args);
    }
}

Or create a separate process application class:

@ProcessApplication("my-process-application")
@Component
public class MyProcessApplication extends SpringBootProcessApplication {
    // This ensures proper context switching
}

4. Alternative Configuration Approach

If the above doesn’t work, try using @Service instead of @Component and ensure component scanning:

@Service("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
    // Implementation
}

Add explicit component scanning to your main application class:

@SpringBootApplication
@ComponentScan(basePackages = {"your.package.path"})
@EnableProcessApplication
public class CamundaApplication {
    // Main method
}

5. Verify BPMN Configuration

Ensure your BPMN service task is correctly configured:

<bpmn:serviceTask id="Task_CheckUser" 
                  name="Check if User is valid" 
                  camunda:asyncBefore="true" 
                  camunda:delegateExpression="${userValidationDelegate}">
    <bpmn:extensionElements>
        <camunda:failedJobRetryTimeCycle>R3/PT5M</camunda:failedJobRetryTimeCycle>
    </bpmn:extensionElements>
    <bpmn:incoming>Flow_To_UserCheck</bpmn:incoming>
    <bpmn:outgoing>Flow_To_IsUserValid</bpmn:outgoing>
</bpmn:serviceTask>

6. Debugging Steps

To troubleshoot further:

  1. Check bean registration: Add logging to verify your bean is being created:

    @PostConstruct
    public void init() {
        log.info("UserValidationDelegate bean initialized");
    }
    
  2. Verify Spring context: Add a @Component that lists all beans at startup:

    @Component
    public class BeanLister implements ApplicationListener<ContextRefreshedEvent> {
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            String[] beanNames = event.getApplicationContext().getBeanDefinitionNames();
            log.info("Available beans: {}", Arrays.toString(beanNames));
        }
    }
    
  3. Test with Java class reference: Temporarily change your BPMN to use camunda:class instead of camunda:delegateExpression to verify the class works:

    camunda:class="com.yourpackage.UserValidationDelegate"
    

Common Pitfalls to Avoid

  • Deployment timing: The bean must be available when the process is executed, not just when deployed[3]
  • Context switching: Ensure your process application is properly registered to maintain Spring context[2]
  • Classpath issues: Verify all dependencies are available at runtime[3]
  • Async execution: When using camunda:asyncBefore="true", ensure the job executor has access to the Spring context[5]

Alternative Workaround

If the issue persists, consider using a service locator pattern:

@Component("userValidationDelegate")
public class UserValidationDelegate implements JavaDelegate {
    
    @Override
    public void execute(DelegateExecution execution) throws Exception {
        // Get Spring context manually if needed
        ApplicationContext context = SpringContextHolder.getApplicationContext();
        YourService service = context.getBean(YourService.class);
        // Use the service
    }
}

This solution addresses the core issue of Spring bean resolution in Camunda delegate expressions and provides multiple approaches to ensure your delegate is properly recognized by the process engine.

Hi @aravindhrs,

Thanks for your reply!
I tried above given suggestion but it is still not working and giving the same error.

I tried using directly class name but that gave me another error as below -

org.camunda.bpm.engine.ProcessEngineException: ENGINE-09008 Exception while instantiating class ‘com.bpm.delegate.UserValidationDelegate’: ENGINE-09017 Cannot load class ‘com.bpm.delegate.UserValidationDelegate’: com.bpm.delegate.UserValidationDelegate

Hi @aravindhrs,

Aplogies for replying late on this thread. I have found the root cause of the above issue. Basically I was using two microservices having separate camunda engine but having shared database. Since I have marked service task as ‘async before’ the job information was getting persisted in database. But second application (say appB) job exceutor was picking up the job. Since application was not having delegate information of appA so it was failing with above error.

For solution I have added process-engine-name in both application and explicitly telling job executor to pick jobs only which is persisted by specific process engine.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.