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
- Guard the call site: only invoke addShutdownTask when the Runnable is non-null.
- Fix the task supplier so it returns a no-op Runnable instead of null.
- 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
- Null-check tasks before registering
- Return no-op Runnables from optional task factories instead of null
- Audit recorders for conditional shutdown task wiring
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
- Extension passed an invalid last shutdown handler
- Failed to load steps from %s
- A build step must be a non-static method: %s
- java.lang.InterruptedException (wrapped)
- The application is stopping
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/0080d70781dac959.
Report an issue: GitHub.