quarkusio/quarkus · error · IllegalStateException

Job [{identity}] that was previously scheduled programmatica

Error message

Job [{identity}] that was previously scheduled programmatically cannot be disabled

What it means

When a scheduled application's job is disabled (@Scheduled{enabled=false} or equivalent) but a trigger for the same identity already exists in the Quartz store from a previous programmatic schedule, the scheduler cannot reconcile: it refuses to silently disable a job previously scheduled programmatically and throws IllegalStateException.

Source

Thrown at extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzSchedulerImpl.java:1021

                jobBuilder.usingJobData(EXECUTION_METADATA_SKIP_PREDICATE_CLASS,
                        executionMetadata.skipPredicateClass().getName());
            }
        }

        JobDetail jobDetail = jobBuilder.requestRecovery().build();

        org.quartz.Trigger trigger;
        Optional<TriggerBuilder<?>> triggerBuilder = createTrigger(scheduled.identity(), scheduled, runtimeConfig,
                jobDetail, description);
        if (triggerBuilder.isPresent()) {
            if (oldTrigger != null) {
                trigger = triggerBuilder.get().startAt(oldTrigger.getNextFireTime()).build();
            } else {
                trigger = triggerBuilder.get().build();
            }
        } else {
            if (oldTrigger != null) {
                throw new IllegalStateException(
                        "Job [" + scheduled.identity() + "] that was previously scheduled programmatically cannot be disabled");
            }
            // Job is disabled
            return null;
        }

        JobInstrumenter instrumenter = null;
        if (schedulerConfig.tracingEnabled() && jobInstrumenter.isResolvable()) {
            instrumenter = jobInstrumenter.get();
        }
        invoker = initInvoker(invoker, events, scheduled.concurrentExecution(), skipPredicate, instrumenter,
                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 {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Delete the stale trigger/job from the Quartz store (scheduler.deleteJob) or clean the QRTZ_ tables for that identity before disabling
  2. Re-enable the job and remove it programmatically (unschedule) instead of using the disabled flag
  3. Rename the job identity so the disabled definition no longer collides with the stored trigger

Example fix

// before: disabled job with stale trigger
@Scheduled(identity="reports", disabled=true) ...
// after: unschedule programmatically instead
scheduler.unscheduleJob("reports");
Defensive patterns

Strategy: try-catch

Validate before calling

// check for an existing stored trigger before relying on disabled config
org.quartz.Trigger existing = scheduler.getTrigger(new TriggerKey(identity, Scheduler.class.getName()));
boolean safeToDisable = existing == null;

Try / catch

try {
    schedulerInstance.schedule(...);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("cannot be disabled")) { scheduler.deleteJob(jobKey); }
}

Prevention

When it happens

Trigger: Disabling a job (e.g. `quarkus.scheduler.enabled=false` per-job config or property-driven enabled=false) while the DB/job-store still holds a trigger with that identity created earlier by programmatic scheduling.

Common situations: Toggling a job off via config between deploys with a JDBC store; a job previously registered at runtime then disabled in code; stale triggers left in the DB after refactoring.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/5af952c2740930f6. Report an issue: GitHub.