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
- Delete stale Quartz job rows whose JobDataMap references the removed/renamed SkipPredicate and restart so Quarkus re-registers jobs from code
- Restore the SkipPredicate class or revert the rename/move so the stored FQCN resolves
- 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)
- 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
- Keep @SkipPredicate implementations in stable packages
- Purge persisted Quartz jobs after deleting predicates
- Register predicate classes for native-image reflection when needed
- Diff stored JobDataMap class names against the current codebase after upgrades
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
- Cannot load task class: {taskClass}
- Cannot load async task class: {taskClass}
- Thread pool class not found: ${threadPoolClass}
- Unable to load handled exception type ${i.getProvidedType()}
- Unable to load type: ${name}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/874c96a41e947e8b.
Report an issue: GitHub.