quarkusio/quarkus · error · UnsupportedOperationException

shutdown not allowed on managed executor service

Error message

shutdown not allowed on managed executor service

What it means

DelegatingExecutorService wraps a Quarkus-managed executor (backed by virtual threads or the container-managed thread pool). Because the lifecycle of the underlying executor is owned by Quarkus, callers must not shut it down; shutdown() therefore throws UnsupportedOperationException unconditionally.

Source

Thrown at extensions/virtual-threads/runtime/src/main/java/io/quarkus/virtual/threads/DelegatingExecutorService.java:39

        return delegate;
    }

    public boolean isShutdown() {
        // container managed executors are never shut down from the application's perspective
        return false;
    }

    public boolean isTerminated() {
        // container managed executors are never shut down from the application's perspective
        return false;
    }

    public boolean awaitTermination(final long timeout, final TimeUnit unit) {
        return false;
    }

    public void shutdown() {
        throw new UnsupportedOperationException("shutdown not allowed on managed executor service");
    }

    public List<Runnable> shutdownNow() {
        throw new UnsupportedOperationException("shutdownNow not allowed on managed executor service");
    }

    public String toString() {
        return delegate.toString();
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the shutdown()/shutdownNow() call — Quarkus shuts the executor down itself
  2. Inject/accept a dedicated application-owned ExecutorService (e.g. Executors.newVirtualThreadPerTaskExecutor()) where lifecycle shutdown is required
  3. Wrap the managed executor so consuming third-party code never sees lifecycle methods

Example fix

// before
@PreDestroy
void stop() { executor.shutdown(); } // UnsupportedOperationException
// after
@PreDestroy
void stop() { /* managed by Quarkus; nothing to do */ }
Defensive patterns

Strategy: validation

Validate before calling

if (executor instanceof DelegatingExecutorService || isQuarkusManaged(executor)) { /* skip shutdown */ }

Type guard

boolean isManagedExecutor(ExecutorService es) { return es.getClass().getName().startsWith("io.quarkus.virtual.threads"); }

Try / catch

try { executor.shutdown(); } catch (UnsupportedOperationException ignored) { /* managed by Quarkus */ }

Prevention

When it happens

Trigger: Invoking shutdown() on the ExecutorService returned by Quarkus' managed executor injection or the virtual-threads support (e.g. from an @ApplicationScoped bean holding the executor and attempting cleanup in @PreDestroy/stop logic).

Common situations: Application shutdown hooks or library code (e.g. closing a client that accepts an ExecutorService) that calls executor.shutdown() out of habit on an injected managed executor.

Related errors


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