{"record":{"id":"a79c47ac6ad1bb26","repo":"grpc/grpc-java","slug":"the-transport-factory-is-closed-a79c47","errorCode":null,"errorMessage":"The transport factory is closed.","messagePattern":"The transport factory is closed\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"inprocess/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java","lineNumber":279,"sourceCode":"    private final boolean includeCauseWithStatus;\n    private long assumedMessageSize;\n\n    private InProcessClientTransportFactory(\n        @Nullable ScheduledExecutorService scheduledExecutorService,\n        int maxInboundMetadataSize, boolean includeCauseWithStatus, long assumedMessageSize) {\n      useSharedTimer = scheduledExecutorService == null;\n      timerService = useSharedTimer\n          ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : scheduledExecutorService;\n      this.maxInboundMetadataSize = maxInboundMetadataSize;\n      this.includeCauseWithStatus = includeCauseWithStatus;\n      this.assumedMessageSize = assumedMessageSize;\n    }\n\n    @Override\n    public ConnectionClientTransport newClientTransport(\n        SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {\n      if (closed) {\n        throw new IllegalStateException(\"The transport factory is closed.\");\n      }\n      // TODO(carl-mastrangelo): Pass channelLogger in.\n      return new InProcessTransport(\n          addr, maxInboundMetadataSize, options.getAuthority(), options.getUserAgent(),\n          options.getEagAttributes(), includeCauseWithStatus, assumedMessageSize);\n    }\n\n    @Override\n    public ScheduledExecutorService getScheduledExecutorService() {\n      return timerService;\n    }\n\n    @Override\n    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {\n      return null;\n    }\n\n    @Override","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/inprocess/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java#L261-L297","documentation":"InProcessChannelBuilder's transport factory throws IllegalStateException once closed, i.e. after the built channel has been shut down. It prevents creating new InProcessTransports on a torn-down builder.","triggerScenarios":"Issuing a new RPC through a channel after channel shutdown, so newClientTransport runs with closed==true.","commonSituations":"Reusing a channel after shutdown()/awaitTermination; lifecycle races where a shutdown completes while new requests are still arriving.","solutions":["Build a fresh channel with InProcessChannelBuilder.forName(...) after the old one is shut down.","Issue all RPCs before calling shutdown, or hold the channel open while it's needed.","Serialize shutdown against new-request submission in application code."],"exampleFix":"// before\nchannel.shutdown();\nstub.call(req); // IllegalStateException\n// after\nstub.call(req);\nchannel.shutdown();","handlingStrategy":"validation","validationCode":"if (channel.isShutdown() || channel.isTerminated()) { channel = InProcessChannelBuilder.forName(serverName).build(); }","typeGuard":null,"tryCatchPattern":"try { stub.call(req); } catch (IllegalStateException e) { if (e.getMessage().contains(\"transport factory is closed\")) { channel = InProcessChannelBuilder.forName(serverName).build(); } else { throw e; } }","preventionTips":["Check isShutdown() before RPCs","Rebuild channels after shutdown","Centralize channel lifecycle management"],"tags":["grpc","inprocess","lifecycle","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}