{"record":{"id":"70b1bb391e752c1a","repo":"quarkusio/quarkus","slug":"cannot-load-async-task-class-taskclass","errorCode":null,"errorMessage":"Cannot load async task class: {taskClass}","messagePattern":"Cannot load async task class: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzSchedulerImpl.java","lineNumber":1300,"sourceCode":"\n        @SuppressWarnings(\"unchecked\")\n        public SerializedExecutionMetadata(JobDetail jobDetail) {\n            ClassLoader tccl = Thread.currentThread().getContextClassLoader();\n            String taskClassStr = jobDetail.getJobDataMap().getString(EXECUTION_METADATA_TASK_CLASS);\n            try {\n                this.taskClass = taskClassStr != null\n                        ? (Class<? extends Consumer<ScheduledExecution>>) tccl.loadClass(taskClassStr)\n                        : null;\n            } catch (ClassNotFoundException e) {\n                throw new IllegalStateException(\"Cannot load task class: \" + taskClassStr);\n            }\n            String asyncTaskClassStr = jobDetail.getJobDataMap().getString(EXECUTION_METADATA_ASYNC_TASK_CLASS);\n            try {\n                this.asyncTaskClass = asyncTaskClassStr != null\n                        ? (Class<? extends Function<ScheduledExecution, Uni<Void>>>) tccl.loadClass(asyncTaskClassStr)\n                        : null;\n            } catch (ClassNotFoundException e) {\n                throw new IllegalStateException(\"Cannot load async task class: \" + taskClassStr);\n            }\n\n            String skipPredicateClassStr = jobDetail.getJobDataMap().getString(EXECUTION_METADATA_SKIP_PREDICATE_CLASS);\n            try {\n                this.skipPredicateClass = skipPredicateClassStr != null\n                        ? (Class<? extends SkipPredicate>) tccl.loadClass(skipPredicateClassStr)\n                        : null;\n            } catch (ClassNotFoundException e) {\n                throw new IllegalStateException(\"Cannot load skip predicate class: \" + taskClassStr);\n            }\n            this.runOnVirtualThread = Boolean\n                    .parseBoolean(jobDetail.getJobDataMap().getString(EXECUTION_METADATA_RUN_ON_VIRTUAL_THREAD));\n            this.nonconcurrent = Boolean.parseBoolean(jobDetail.getJobDataMap().getString(EXECUTION_METADATA_NONCONCURRENT));\n        }\n\n        @Override\n        public Consumer<ScheduledExecution> task() {\n            return taskClass != null ? SchedulerUtils.instantiateBeanOrClass(taskClass) : null;","sourceCodeStart":1282,"sourceCodeEnd":1318,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/QuartzSchedulerImpl.java#L1282-L1318","documentation":"Analogous to the sync task class, QuartzSchedulerImpl stores the async task class (Function<ScheduledExecution, Uni<Void>>) FQCN in the JobDataMap and loads it via TCCL when the job fires. If loading fails with ClassNotFoundException it throws 'Cannot load async task class: <name>'. Note the message uses the wrong variable (taskClassStr instead of asyncTaskClassStr), so the printed name may be misleading — inspect the actual JobDataMap entry.","triggerScenarios":"A @Scheduled method returning Uni (non-blocking) was persisted to a Quartz job store; on later execution the stored EXECUTION_METADATA_ASYNC_TASK_CLASS FQCN cannot be loaded through the current TCCL.","commonSituations":"Async scheduled method renamed/refactored/removed after jobs were persisted in a JDBC store; native-image or dev-mode classloader lacking the previously stored class; stale Quartz DB rows from an older deployment version.","solutions":["Purge stale Quartz job rows (QRTZ_JOB_DETAILS/QRTZ_TRIGGERS) referencing the old async task class so jobs are recreated from current code","Redeploy/revert the class rename so the stored FQCN resolves on the classpath","Inspect the EXECUTION_METADATA_ASYNC_TASK_CLASS entry in the JobDataMap to see the exact missing class (the error text may show the wrong name due to a message bug)","Verify the class is not excluded from native-image or a different classloader in your runtime setup"],"exampleFix":"// before: stale row holds com.acme.OldAsyncTask after rename\n// after: clear the stale persisted job and let Quarkus re-register\n// SQL: DELETE FROM QRTZ_JOB_DETAILS WHERE JOB_NAME = 'com.acme.OldAsyncTask';\n// then restart the application so @Scheduled methods are re-scheduled","handlingStrategy":"validation","validationCode":"// Check the async task FQCN stored in the job data map resolves\nString async = jobDetail.getJobDataMap().getString(\"io.quarkus.quartz.async_task_class\");\nif (async != null) {\n    try { Class.forName(async, false, Thread.currentThread().getContextClassLoader()); }\n    catch (ClassNotFoundException e) { /* delete stale job row and re-register */ }\n}","typeGuard":null,"tryCatchPattern":"try {\n    executeJob();\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Cannot load async task class\")) {\n        LOG.error(\"Stored async task class missing; purging stale Quartz rows recommended\");\n    }\n    throw e;\n}","preventionTips":["Don't rename Uni-returning @Scheduled methods while using a persistent job store","Purge QRTZ tables across deployments that change scheduled method signatures","Pin datasource/store-type per environment to avoid leftover rows","Test scheduled jobs after every refactor in dev mode with a clean DB"],"tags":["quarkus","quartz","classpath","classnotfound","persistent-job-store","async"],"backgroundTag":"classnotfound-stale-job-store","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}