quarkusio/quarkus · error · UnableToInterruptJobException

Job ${beanClass} can not be interrupted, since it does not i

Error message

Job ${beanClass} can not be interrupted, since it does not implement org.quartz.InterruptableJob

What it means

CdiAwareJob wraps CDI-managed @Scheduled job beans. Its interrupt() delegates to the job only if the bean class implements org.quartz.InterruptableJob; otherwise it throws UnableToInterruptJobException with this message. Quartz invokes interrupt() when a job is interrupted via scheduler.interrupt(jobKey) or on scheduler shutdown with interruption requested.

Source

Thrown at extensions/quartz/runtime/src/main/java/io/quarkus/quartz/runtime/CdiAwareJob.java:51

        try {
            beanInstance.execute(context);
        } catch (JobExecutionException e) {
            refire = e.refireImmediately();
            throw e;
        } finally {
            if (refire != true && handle.getBean().getScope().equals(Dependent.class)) {
                handle.destroy();
            }
        }
    }

    @Override
    public void interrupt() throws UnableToInterruptJobException {
        // delegate if possible; throw an exception in other cases
        if (InterruptableJob.class.isAssignableFrom(handle.getBean().getBeanClass())) {
            ((InterruptableJob) beanInstance).interrupt();
        } else {
            throw new UnableToInterruptJobException("Job " + handle.getBean().getBeanClass()
                    + " can not be interrupted, since it does not implement " + InterruptableJob.class.getName());
        }
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the job bean implement org.quartz.InterruptableJob and handle interruption in interrupt().
  2. Redesign the job to check a cancellation flag/thread interruption instead of relying on Quartz interrupt.
  3. Catch UnableToInterruptJobException at the interrupt call site and handle non-interruptible jobs gracefully.

Example fix

// before
@Scheduled(cron = "0 0 * * * ?")
public class ReportJob { public void run() { ... } }

// after
@Scheduled(cron = "0 0 * * * ?")
public class ReportJob implements org.quartz.InterruptableJob {
    public void execute(JobExecutionContext ctx) { while (!Thread.currentThread().isInterrupted()) { ... } }
    public void interrupt() { Thread.currentThread().interrupt(); }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// check before requesting interruption
Class<?> beanClass = jobHandle.getBean().getBeanClass();
boolean interruptible = org.quartz.InterruptableJob.class.isAssignableFrom(beanClass);

Type guard

static boolean isInterruptableJob(Class<?> beanClass) {
    return beanClass != null && org.quartz.InterruptableJob.class.isAssignableFrom(beanClass);
}

Try / catch

try {
    scheduler.interrupt(jobKey);
} catch (org.quartz.UnableToInterruptJobException e) {
    // job does not implement InterruptableJob: stop it another way
    log.warn("Job not interruptible: " + jobKey);
} catch (org.quartz.SchedulerException e) {
    log.error("Failed to interrupt job", e);
}

Prevention

When it happens

Trigger: Call scheduler.interrupt(jobKey) (or trigger shutdown-with-interrupt) for a @Scheduled CDI job bean whose class does not implement InterruptableJob.

Common situations: Attempting to cancel long-running scheduled methods programmatically; stopping the app while a long job runs; assuming all Quartz jobs are interruptible.

Related errors


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