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

  1. Call execute(runnableOrConsumer) on the JobDefinition before schedule().
  2. For non-blocking work, call setAsyncTask(...) instead.
  3. If the job is conditional, skip the entire newJob(...).schedule() chain when no task exists rather than scheduling an empty job.
  4. 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

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


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