{"record":{"id":"7bcee2a4fc81ae5d","repo":"grpc/grpc-java","slug":"restricted-shutdown-is-not-allowed","errorCode":null,"errorMessage":"Restricted: shutdown() is not allowed","messagePattern":"Restricted: shutdown\\(\\) is not allowed","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/grpc/internal/ManagedChannelImpl.java","lineNumber":2189,"sourceCode":"    @Override\n    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException {\n      return delegate.invokeAny(tasks, timeout, unit);\n    }\n\n    @Override\n    public boolean isShutdown() {\n      return delegate.isShutdown();\n    }\n\n    @Override\n    public boolean isTerminated() {\n      return delegate.isTerminated();\n    }\n\n    @Override\n    public void shutdown() {\n      throw new UnsupportedOperationException(\"Restricted: shutdown() is not allowed\");\n    }\n\n    @Override\n    public List<Runnable> shutdownNow() {\n      throw new UnsupportedOperationException(\"Restricted: shutdownNow() is not allowed\");\n    }\n\n    @Override\n    public <T> Future<T> submit(Callable<T> task) {\n      return delegate.submit(task);\n    }\n\n    @Override\n    public Future<?> submit(Runnable task) {\n      return delegate.submit(task);\n    }\n\n    @Override","sourceCodeStart":2171,"sourceCodeEnd":2207,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java#L2171-L2207","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nexecutorService.shutdown(); // wrapped channel executor\n// after\nchannel.shutdown();","handlingStrategy":"try-catch","validationCode":"if (executor instanceof io.grpc.internal.ManagedChannelImplAccessor) { /* restricted */ } // otherwise track ownership: only shut down executors you created","typeGuard":null,"tryCatchPattern":"try {\n  executor.shutdown();\n} catch (UnsupportedOperationException e) {\n  // channel-owned executor; shut down the channel instead\n  channel.shutdown();\n}","preventionTips":["Treat executors obtained from gRPC internals as channel-owned.","Only shut down executors your code explicitly created.","Use ManagedChannel.shutdown()/shutdownNow() for teardown."],"tags":["grpc","lifecycle","executor","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}