grpc/grpc-java · error · IllegalStateException

The transport factory is closed.

Error message

The transport factory is closed.

What it means

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.

Source

Thrown at inprocess/src/main/java/io/grpc/inprocess/InProcessChannelBuilder.java:279

    private final boolean includeCauseWithStatus;
    private long assumedMessageSize;

    private InProcessClientTransportFactory(
        @Nullable ScheduledExecutorService scheduledExecutorService,
        int maxInboundMetadataSize, boolean includeCauseWithStatus, long assumedMessageSize) {
      useSharedTimer = scheduledExecutorService == null;
      timerService = useSharedTimer
          ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : scheduledExecutorService;
      this.maxInboundMetadataSize = maxInboundMetadataSize;
      this.includeCauseWithStatus = includeCauseWithStatus;
      this.assumedMessageSize = assumedMessageSize;
    }

    @Override
    public ConnectionClientTransport newClientTransport(
        SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
      if (closed) {
        throw new IllegalStateException("The transport factory is closed.");
      }
      // TODO(carl-mastrangelo): Pass channelLogger in.
      return new InProcessTransport(
          addr, maxInboundMetadataSize, options.getAuthority(), options.getUserAgent(),
          options.getEagAttributes(), includeCauseWithStatus, assumedMessageSize);
    }

    @Override
    public ScheduledExecutorService getScheduledExecutorService() {
      return timerService;
    }

    @Override
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
      return null;
    }

    @Override

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Build a fresh channel with InProcessChannelBuilder.forName(...) after the old one is shut down.
  2. Issue all RPCs before calling shutdown, or hold the channel open while it's needed.
  3. Serialize shutdown against new-request submission in application code.

Example fix

// before
channel.shutdown();
stub.call(req); // IllegalStateException
// after
stub.call(req);
channel.shutdown();
Defensive patterns

Strategy: validation

Validate before calling

if (channel.isShutdown() || channel.isTerminated()) { channel = InProcessChannelBuilder.forName(serverName).build(); }

Try / catch

try { stub.call(req); } catch (IllegalStateException e) { if (e.getMessage().contains("transport factory is closed")) { channel = InProcessChannelBuilder.forName(serverName).build(); } else { throw e; } }

Prevention

When it happens

Trigger: Issuing a new RPC through a channel after channel shutdown, so newClientTransport runs with closed==true.

Common situations: Reusing a channel after shutdown()/awaitTermination; lifecycle races where a shutdown completes while new requests are still arriving.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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