quarkusio/quarkus · error · IllegalStateException
Cannot modify a job that was already scheduled
Error message
Cannot modify a job that was already scheduled
What it means
JobDefinition is a fluent, mutable builder whose setters (setCron, setInterval, setDelayed, setConcurrentExecution, setSkipPredicate, setOverdueGracePeriod) are only legal until the job is scheduled. checkScheduled() throws this IllegalStateException once the job has been registered with the scheduler and someone attempts further mutation.
Source
Thrown at extensions/scheduler/common/src/main/java/io/quarkus/scheduler/common/runtime/AbstractJobDefinition.java:152
@Override
public THIS setAsyncTask(Function<ScheduledExecution, Uni<Void>> asyncTask) {
checkScheduled();
if (task != null) {
throw new IllegalStateException("Sync task was already set");
}
this.asyncTask = Objects.requireNonNull(asyncTask);
return self();
}
@Override
public THIS setAsyncTask(Class<? extends Function<ScheduledExecution, Uni<Void>>> asyncTaskClass) {
this.asyncTaskClass = Objects.requireNonNull(asyncTaskClass);
return setAsyncTask(SchedulerUtils.instantiateBeanOrClass(asyncTaskClass));
}
protected void checkScheduled() {
if (scheduled) {
throw new IllegalStateException("Cannot modify a job that was already scheduled");
}
}
@SuppressWarnings("unchecked")
protected THIS self() {
return (THIS) this;
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- To change a scheduled job's config, unschedule and create a new JobDefinition via scheduler.newJob(...) with the updated settings, or use the scheduler's schedule/update flow
- Treat JobDefinition as write-once: finish all setters before submitting
- Do not store and mutate JobDefinition references after scheduling
- For dynamic schedules, use a scheduling implementation that supports job updates (e.g. Quartz-based) and its own APIs
Example fix
// before
jobDef.setCron("0 0 12 * * ?"); // job already scheduled -> throws
// after
scheduler.unscheduleJob("j");
scheduler.newJob("j").setCron("0 0 12 * * ?").setTask(task).schedule(); Defensive patterns
Strategy: validation
Validate before calling
if (definition.isScheduled()) { // track via your own wrapper
throw new IllegalStateException("create a new JobDefinition to change config");
}
definition.setCron(newCron); Type guard
boolean modifiable(JobDefinition d) {
return !scheduledJobs.contains(d); // maintain registry of scheduled definitions
} Try / catch
try {
definition.setCron(newCron);
} catch (IllegalStateException e) {
scheduler.unscheduleJob(id);
scheduler.newJob(id).setCron(newCron).setTask(task).schedule();
} Prevention
- Treat JobDefinition as write-once; set everything before scheduling
- Never cache/mutate JobDefinition references after submission
- Recreate definitions for updates instead of mutating
- Encapsulate rescheduling logic in a helper that always builds a fresh definition
When it happens
Trigger: Calling any setter on a JobDefinition after the definition was scheduled (the scheduled flag is set), e.g. keeping a reference and later calling setCron("0 0 * * * ?") to 'update' the job.
Common situations: Trying to dynamically reschedule a job through the old definition object instead of the scheduler's update API; caching JobDefinition instances in a service and mutating them at runtime; tests reconfiguring an already-submitted job.
Related errors
- Could not evaluate standby mode
- Async task was already set
- Sync task was already set
- Can only sync state on the server side of remote dev mode
- All parameters have already been loaded, it is too late to c
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/47abf36273d9aafe.
Report an issue: GitHub.