Currently, when a JobExecutionAlreadyRunningException happens, the message of the exception is inaccurate. It shows the job instance details instead of the job execution details.
package org.springframework.batch.samples.helloworld;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.parameters.JobParameters;
import org.springframework.batch.core.job.parameters.JobParametersBuilder;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.infrastructure.repeat.RepeatStatus;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
@Configuration
@EnableBatchProcessing
public class HelloWorldJobConfiguration {
public static void main(String[] args) throws Exception {
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldJobConfiguration.class);
JobOperator jobOperator = context.getBean(JobOperator.class);
Job job = context.getBean(Job.class);
JobParameters parameters = new JobParametersBuilder().addString("name", "foo").toJobParameters();
jobOperator.start(job, parameters);
// attempt to start the same job instance, which should fail with JobExecutionAlreadyRunningException
jobOperator.start(job, parameters);
}
@Bean
public Step step(JobRepository jobRepository) {
return new StepBuilder(jobRepository).tasklet((contribution, chunkContext) -> {
Thread.sleep(5000);
System.out.println("Hello world!");
return RepeatStatus.FINISHED;
}).build();
}
@Bean
public Job job(JobRepository jobRepository, Step step) {
return new JobBuilder(jobRepository).start(step).build();
}
@Bean
public TaskExecutor taskExecutor() {
return new SimpleAsyncTaskExecutor("batch-worker-");
}
}
prints:
08:17:24.110 [batch-worker-1] INFO o.s.b.c.l.s.TaskExecutorJobLauncher - Job: [SimpleJob: [name=job]] launched with the following parameters: [{JobParameter{name='name', value=foo, type=class java.lang.String, identifying=true}}]
08:17:24.115 [batch-worker-1] INFO o.s.batch.core.job.SimpleStepHandler - Executing step: [step]
Exception in thread "main" org.springframework.batch.core.launch.JobExecutionAlreadyRunningException: A job execution for this job is already running: JobExecution: id=1, version=null, startTime=2026-02-12T08:17:24.115279, endTime=null, lastUpdated=2026-02-12T08:17:24.115295, status=STARTED, exitStatus=exitCode=UNKNOWN;exitDescription=, job=[JobInstance: id=1, version=null, Job=[job]], jobParameters=[{JobParameter{name='name', value=foo, type=class java.lang.String, identifying=true}}]
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.createJobExecution(TaskExecutorJobLauncher.java:136)
at org.springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:108)
at org.springframework.batch.core.launch.support.SimpleJobOperator.start(SimpleJobOperator.java:201)
at org.springframework.batch.core.launch.support.TaskExecutorJobOperator.start(TaskExecutorJobOperator.java:117)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:565)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:158)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:133)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:371)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:222)
at jdk.proxy2/jdk.proxy2.$Proxy16.start(Unknown Source)
at org.springframework.batch.samples.helloworld.HelloWorldJobConfiguration.main(HelloWorldJobConfiguration.java:46)
Hello world!
08:17:29.127 [batch-worker-1] INFO o.s.batch.core.step.AbstractStep - Step: [step] executed in 5s10ms
08:17:29.129 [batch-worker-1] INFO o.s.b.c.l.s.TaskExecutorJobLauncher - Job: [SimpleJob: [name=job]] completed with the following parameters: [{JobParameter{name='name', value=foo, type=class java.lang.String, identifying=true}}] and the following status: [COMPLETED] in 5s12ms
This code should be updated to pass the running job execution instead of the job instance.
Currently, when a
JobExecutionAlreadyRunningExceptionhappens, the message of the exception is inaccurate. It shows the job instance details instead of the job execution details.prints:
This code should be updated to pass the running job execution instead of the job instance.