quarkusio/quarkus · error · IllegalArgumentException

Extension passed an invalid shutdown handler

Error message

Extension passed an invalid shutdown handler

What it means

StartupContext's ShutdownContext.addShutdownTask throws IllegalArgumentException when a null Runnable is passed, because a null shutdown task is always an extension bug — there is nothing to run at shutdown.

Source

Thrown at core/runtime/src/main/java/io/quarkus/runtime/StartupContext.java:37

    // Holds values for returned proxies
    // These values are usually returned from recorder methods but can be also set explicitly
    // For example, the raw command line args and ShutdownContext are set when the StartupContext is created
    private final Map<String, Object> values = new HashMap<>();

    private final Deque<Runnable> shutdownTasks = new ConcurrentLinkedDeque<>();
    private final Deque<Runnable> lastShutdownTasks = new ConcurrentLinkedDeque<>();
    private String[] commandLineArgs;
    private String currentBuildStepName;

    public StartupContext() {
        ShutdownContext shutdownContext = new ShutdownContext() {
            @Override
            public void addShutdownTask(Runnable runnable) {
                if (runnable != null) {
                    shutdownTasks.addFirst(runnable);
                } else {
                    throw new IllegalArgumentException("Extension passed an invalid shutdown handler");
                }
            }

            @Override
            public void addLastShutdownTask(Runnable runnable) {
                if (runnable != null) {
                    lastShutdownTasks.addFirst(runnable);
                } else {
                    throw new IllegalArgumentException("Extension passed an invalid last shutdown handler");
                }
            }
        };
        values.put(ShutdownContext.class.getName(), shutdownContext);
        values.put(RAW_COMMAND_LINE_ARGS, new Supplier<String[]>() {
            @Override
            public String[] get() {
                if (commandLineArgs == null) {
                    throw new RuntimeException("Command line arguments not available during static init");

View on GitHub (pinned to e1c734241f)

Solutions

  1. Guard the call site: only invoke addShutdownTask when the Runnable is non-null.
  2. Fix the task supplier so it returns a no-op Runnable instead of null.
  3. Check which extension passes the null task by inspecting the startup/shutdown stack trace.

Example fix

// before
shutdownContext.addShutdownTask(maybeTask());
// after
Runnable t = maybeTask();
if (t != null) { shutdownContext.addShutdownTask(t); }
Defensive patterns

Strategy: validation

Validate before calling

if (runnable != null) { shutdownContext.addShutdownTask(runnable); }

Prevention

When it happens

Trigger: Calling shutdownContext.addShutdownTask(null), typically when a lazily-computed Runnable evaluates to null (e.g. an optional resource, a config-conditional task).

Common situations: Extension recorders wiring shutdown tasks whose supplier returns null when a feature is disabled; refactoring that made a task conditional without guarding the call.

Related errors


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