quarkusio/quarkus · error · java.lang.IllegalStateException
Either sync or async task must be set
Error message
Either sync or async task must be set
What it means
The programmatic JobDefinition built via Scheduler.newJob() must specify what to execute: either a synchronous task (execute(...)) or an async task (setAsyncTask(...)). schedule() validates this and throws IllegalStateException if both are unset — a job with no body can never run.
Source
Thrown at extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/runtime/SimpleScheduler.java:633
return scheduledFireTime.toInstant();
}
}
public class SimpleJobDefinition extends AbstractJobDefinition<SimpleJobDefinition> {
private final SchedulerConfig schedulerConfig;
SimpleJobDefinition(String id, SchedulerConfig schedulerConfig) {
super(id);
this.schedulerConfig = schedulerConfig;
}
@Override
public Trigger schedule() {
checkScheduled();
if (task == null && asyncTask == null) {
throw new IllegalStateException("Either sync or async task must be set");
}
scheduled = true;
ScheduledInvoker invoker;
if (task != null) {
// Use the default invoker to make sure the CDI request context is activated
invoker = new DefaultInvoker() {
@Override
public CompletionStage<Void> invokeBean(ScheduledExecution execution) {
try {
task.accept(execution);
return CompletableFuture.completedStage(null);
} catch (Exception e) {
return CompletableFuture.failedStage(e);
}
}
@Override
public boolean isRunningOnVirtualThread() {View on GitHub (pinned to e1c734241f)
Solutions
- Call execute(runnableOrConsumer) on the JobDefinition before schedule().
- For non-blocking work, call setAsyncTask(...) instead.
- If the job is conditional, skip the entire newJob(...).schedule() chain when no task exists rather than scheduling an empty job.
- Validate the task reference is non-null before building the JobDefinition.
Example fix
// before
scheduler.newJob("cleanup").setCron("0 0 2 * * ?").schedule();
// after
scheduler.newJob("cleanup").setCron("0 0 2 * * ?").execute(cleanupTask).schedule(); Defensive patterns
Strategy: validation
Validate before calling
Runnable task = resolveTask();
Objects.requireNonNull(task, "Job task must not be null before scheduling");
scheduler.newJob("id").setCron("0 0 2 * * ?").execute(task).schedule(); Try / catch
try {
definition.schedule();
} catch (IllegalStateException e) {
if (!e.getMessage().contains("sync or async task")) throw e;
log.error("Job definition has no execute()/setAsyncTask() set");
} Prevention
- Chain .execute(...) or .setAsyncTask(...) immediately after newJob(...)
- Skip creating the definition entirely when no task is available
- Unit-test programmatic job registration paths
When it happens
Trigger: Calling scheduler.newJob("id").setCron("...")....schedule() without ever invoking execute(task) or setAsyncTask(asyncTask); or setting a task conditionally that ended up null.
Common situations: Programmatic job registration built dynamically where the runnable is resolved from DI/config and can be null; refactoring that removed the execute() call; copy-pasted builder code missing the task line.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- Quartz scheduler is either explicitly disabled through quark
- Unable to instantiate the class:
- Could not expand value %s in property %s
- Invalid () expression on:
- Something went wrong during parameter type resolution - expe
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/19e968fd542b2cd7.
Report an issue: GitHub.