quarkusio/quarkus · error · IllegalStateException

Cannot load skip predicate class: {taskClass}

Error message

Cannot load skip predicate class: {taskClass}

What it means

Quartz jobs persisted in a store also carry the SkipPredicate class FQCN (used to decide whether a run should be skipped, e.g. @SkipPredicate). When the job executes, QuartzSchedulerImpl loads this class via TCCL and throws 'Cannot load skip predicate class: <name>' on ClassNotFoundException. The message interpolates the wrong variable (taskClassStr), so verify the actual EXECUTION_METADATA_SKIP_PREDICATE_CLASS value in the JobDataMap.

Source

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

            } catch (ClassNotFoundException e) {
                throw new IllegalStateException("Cannot load task class: " + taskClassStr);
            }
            String asyncTaskClassStr = jobDetail.getJobDataMap().getString(EXECUTION_METADATA_ASYNC_TASK_CLASS);
            try {
                this.asyncTaskClass = asyncTaskClassStr != null
                        ? (Class<? extends Function<ScheduledExecution, Uni<Void>>>) tccl.loadClass(asyncTaskClassStr)
                        : null;
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException("Cannot load async task class: " + taskClassStr);
            }

            String skipPredicateClassStr = jobDetail.getJobDataMap().getString(EXECUTION_METADATA_SKIP_PREDICATE_CLASS);
            try {
                this.skipPredicateClass = skipPredicateClassStr != null
                        ? (Class<? extends SkipPredicate>) tccl.loadClass(skipPredicateClassStr)
                        : null;
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException("Cannot load skip predicate class: " + taskClassStr);
            }
            this.runOnVirtualThread = Boolean
                    .parseBoolean(jobDetail.getJobDataMap().getString(EXECUTION_METADATA_RUN_ON_VIRTUAL_THREAD));
            this.nonconcurrent = Boolean.parseBoolean(jobDetail.getJobDataMap().getString(EXECUTION_METADATA_NONCONCURRENT));
        }

        @Override
        public Consumer<ScheduledExecution> task() {
            return taskClass != null ? SchedulerUtils.instantiateBeanOrClass(taskClass) : null;
        }

        @Override
        public Class<? extends Consumer<ScheduledExecution>> taskClass() {
            return taskClass;
        }

        @Override
        public Function<ScheduledExecution, Uni<Void>> asyncTask() {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Delete stale Quartz job rows whose JobDataMap references the removed/renamed SkipPredicate and restart so Quarkus re-registers jobs from code
  2. Restore the SkipPredicate class or revert the rename/move so the stored FQCN resolves
  3. Check the EXECUTION_METADATA_SKIP_PREDICATE_CLASS JobDataMap entry for the exact class name (the message text may show the wrong name due to a bug)
  4. Ensure the predicate class is registered for native-image if running a native build

Example fix

// before: predicate deleted while persisted jobs still reference it
// com.acme.OldSkipPredicate (removed)
// after: purge the rows and restart so Quarkus rebuilds job metadata from @SkipPredicate
// SQL: DELETE FROM QRTZ_JOB_DETAILS WHERE JOB_NAME = 'com.acme.OldSkipPredicate';
Defensive patterns

Strategy: validation

Validate before calling

// Validate the skip predicate FQCN stored on the job resolves
String skip = jobDetail.getJobDataMap().getString("io.quarkus.quartz.skip_predicate_class");
if (skip != null) {
    try { Class.forName(skip, false, Thread.currentThread().getContextClassLoader()); }
    catch (ClassNotFoundException e) { /* purge the stale job row before it fires */ }
}

Try / catch

try {
    fireJob();
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Cannot load skip predicate class")) {
        LOG.error("Stored SkipPredicate class missing from classpath");
    }
    throw e;
}

Prevention

When it happens

Trigger: A @Scheduled method with a custom SkipPredicate was persisted to a Quartz job store; at execution time the stored EXECUTION_METADATA_SKIP_PREDICATE_CLASS FQCN can no longer be loaded by the TCCL.

Common situations: SkipPredicate implementation renamed, moved to another package, or deleted while its jobs remain in the JDBC Quartz store; native image not including the predicate class; stale rows from a previous application version.

Related errors


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