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;
}
@OverrideView on GitHub (pinned to 64daddc1f3)
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.
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
- Check isShutdown() before RPCs
- Rebuild channels after shutdown
- Centralize channel lifecycle management
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
- The transport factory is closed.
- Configurators are already set
- Metric with name ${name} already exists
- ScheduledExecutorService not set in Builder
- ChannelLogger is not set in Builder
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/a79c47ac6ad1bb26.
Report an issue: GitHub.