grpc/grpc-java · error · UnsupportedOperationException
Restricted: shutdown() is not allowed
Error message
Restricted: shutdown() is not allowed
What it means
This is thrown by a restricted ExecutorService wrapper inside ManagedChannelImpl (the channel's internal executor view exposed to balancers/listeners). Its shutdown() is deliberately disabled because the executor's lifecycle is owned by the channel itself; shutting it down independently would break the channel.
Source
Thrown at core/src/main/java/io/grpc/internal/ManagedChannelImpl.java:2189
@Override
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return delegate.invokeAny(tasks, timeout, unit);
}
@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);
}
@OverrideView on GitHub (pinned to 64daddc1f3)
Solutions
- Call ManagedChannel.shutdown() (or shutdownNow()) instead of shutting down the restricted executor.
- If you own the underlying executor, shut down the executor you passed to ManagedChannelImplBuilder.executor(...), not the wrapped one.
- Remove cleanup code that closes executors it did not create.
Example fix
// before executorService.shutdown(); // wrapped channel executor // after channel.shutdown();
Defensive patterns
Strategy: try-catch
Validate before calling
if (executor instanceof io.grpc.internal.ManagedChannelImplAccessor) { /* restricted */ } // otherwise track ownership: only shut down executors you created Try / catch
try {
executor.shutdown();
} catch (UnsupportedOperationException e) {
// channel-owned executor; shut down the channel instead
channel.shutdown();
} Prevention
- Treat executors obtained from gRPC internals as channel-owned.
- Only shut down executors your code explicitly created.
- Use ManagedChannel.shutdown()/shutdownNow() for teardown.
When it happens
Trigger: Calling shutdown() on the ExecutorService obtained via channel internals (e.g. Untargetable/Restricted executor returned to LoadBalancer Helpers or via o.grpc.Context storage), instead of shutting down the channel.
Common situations: Custom LoadBalancer or ClientTransportFactory code holding a reference to the channel executor and cleaning it up in its own shutdown path; tests disposing executor references directly.
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
- Restricted: shutdownNow() is not allowed
- Can't set TLS settings for ALTS
- Unsupported operation getPort()
- Not implemented
- Not implemented
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/7bcee2a4fc81ae5d.
Report an issue: GitHub.