quarkusio/quarkus · error · IllegalStateException
A job with this identity is already scheduled: {identity}
Error message
A job with this identity is already scheduled: {identity} What it means
Quarkus Scheduler allows only one task per identity. When scheduling programmatically, `scheduledTasks.putIfAbsent` reveals that an identity is already registered, so an IllegalStateException naming the identity is thrown to prevent duplicate jobs.
Source
Thrown at extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzSchedulerImpl.java:1051
vertx, task != null && runtimeConfig.runBlockingScheduledMethodOnQuartzThread(),
SchedulerUtils.parseExecutionMaxDelayAsMillis(scheduled), blockingExecutor);
QuartzTrigger quartzTrigger = new QuartzTrigger(trigger.getKey(),
new Function<>() {
@Override
public org.quartz.Trigger apply(TriggerKey triggerKey) {
try {
return scheduler.getTrigger(triggerKey);
} catch (SchedulerException e) {
throw new IllegalStateException(e);
}
}
}, invoker,
SchedulerUtils.parseOverdueGracePeriod(scheduled, defaultOverdueGracePeriod),
runtimeConfig.runBlockingScheduledMethodOnQuartzThread(), true, null, description);
QuartzTrigger existing = scheduledTasks.putIfAbsent(scheduled.identity(), quartzTrigger);
if (existing != null) {
throw new IllegalStateException("A job with this identity is already scheduled: " + scheduled.identity());
}
try {
if (oldTrigger != null) {
scheduler.rescheduleJob(trigger.getKey(), trigger);
LOGGER.debugf("Rescheduled job definition with config %s", scheduled);
} else {
scheduler.scheduleJob(jobDetail, trigger);
LOGGER.debugf("Scheduled job definition with config %s", scheduled);
}
} catch (SchedulerException e) {
throw new IllegalStateException(e);
}
return quartzTrigger;
}
/**
* @see NonconcurrentView on GitHub (pinned to e1c734241f)
Solutions
- Use a unique identity per schedule, or check `scheduler.getScheduledJobs()`/unwrap existing QuartzTrigger before scheduling
- Make scheduling idempotent: skip when the identity already exists instead of calling schedule again
- Rename one of the conflicting @Scheduled identities (annotation and programmatic identity must not collide)
Example fix
// before
scheduler.newJob("sync").setEvery("10m").setTask(t -> sync()).schedule(); // second call throws
// after
if (scheduler.getJobByIdentity("sync").isEmpty()) {
scheduler.newJob("sync").setEvery("10m").setTask(t -> sync()).schedule();
} Defensive patterns
Strategy: try-catch
Validate before calling
boolean taken = scheduler.getScheduledJobs().stream()
.anyMatch(j -> j.getSchedule().identity().equals("myJob"));
if (taken) { /* skip or unschedule first */ } Try / catch
try {
scheduler.newJob("myJob").setEvery("10m").setTask(t -> run()).schedule();
} catch (IllegalStateException e) {
if (e.getMessage().contains("already scheduled")) { /* skip duplicate registration */ }
} Prevention
- Generate unique identities or reuse a constant per logical job
- Make re-registration idempotent with an identity check
- Avoid programmatic identities that shadow @Scheduled annotation identities
When it happens
Trigger: Calling `scheduler.newJob("sameId")...schedule()` twice, or scheduling programmatically an identity already defined by a @Scheduled annotation on a bean.
Common situations: Application restart code paths re-registering jobs idempotently; duplicate @Scheduled identity values across beans; test code scheduling the same identity repeatedly within one application instance.
Related errors
- Invalid schedule configuration: {scheduled}
- Either sync or async task must be set
- Clustered jobs configured with unsupported job store option
- The Agroal extension is missing and it is required when a Qu
- Custom JDBC delegate implementation class '%s' was not found
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/066201d7b38294de.
Report an issue: GitHub.