grpc/grpc-java · error · UnsupportedOperationException

Restricted: shutdownNow() is not allowed

Error message

Restricted: shutdownNow() is not allowed

What it means

Same restricted ExecutorService wrapper as the shutdown() case: shutdownNow() is disabled because the channel owns the executor's lifecycle. Calling it throws UnsupportedOperationException to protect channel invariants.

Source

Thrown at core/src/main/java/io/grpc/internal/ManagedChannelImpl.java:2194

    @Override
    public boolean isShutdown() {
      return delegate.isShutdown();
    }

    @Override
    public boolean isTerminated() {
      return delegate.isTerminated();
    }

    @Override
    public void shutdown() {
      throw new UnsupportedOperationException("Restricted: shutdown() is not allowed");
    }

    @Override
    public List<Runnable> shutdownNow() {
      throw new UnsupportedOperationException("Restricted: shutdownNow() is not allowed");
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
      return delegate.submit(task);
    }

    @Override
    public Future<?> submit(Runnable task) {
      return delegate.submit(task);
    }

    @Override
    public <T> Future<T> submit(Runnable task, T result) {
      return delegate.submit(task, result);
    }

    @Override

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Use channel.shutdownNow() to cancel in-flight work; the channel will manage its executor.
  2. Shut down only the executor instance you originally supplied to the builder.
  3. Exclude the channel-wrapped executor from generic executor cleanup loops.

Example fix

// before
List<Runnable> pending = executorService.shutdownNow();
// after
channel.shutdownNow();
Defensive patterns

Strategy: try-catch

Validate before calling

boolean ownsExecutor = createdHere; // track ownership explicitly; never shutdownNow() executors received from gRPC

Try / catch

try {
  executor.shutdownNow();
} catch (UnsupportedOperationException e) {
  channel.shutdownNow();
}

Prevention

When it happens

Trigger: Calling shutdownNow() on the ExecutorService view handed out by ManagedChannelImpl to internal components (balancers, listeners) during custom cleanup or cancellation paths.

Common situations: Custom LoadBalancer teardown code force-stopping all executors it references; generic shutdown hooks iterating over all ExecutorServices in an application.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/77dd342e1dddb007. Report an issue: GitHub.