grpc/grpc-java · error · IllegalStateException
The transport factory is closed.
Error message
The transport factory is closed.
What it means
BinderClientTransportFactory.newClientTransport() checks its closed flag and throws IllegalStateException('The transport factory is closed.') when a new transport is requested after the factory has been shut down. The factory is closed when the owning channelbuilder/channel is disposed, so any late transport creation indicates the channel is being used after (or during) shutdown.
Source
Thrown at binder/src/main/java/io/grpc/binder/internal/BinderClientTransportFactory.java:87
scheduledExecutorPool = checkNotNull(builder.scheduledExecutorPool);
offloadExecutorPool = checkNotNull(builder.offloadExecutorPool);
securityPolicy = checkNotNull(builder.securityPolicy);
bindServiceFlags = checkNotNull(builder.bindServiceFlags);
inboundParcelablePolicy = checkNotNull(builder.inboundParcelablePolicy);
binderDecorator = checkNotNull(builder.binderDecorator);
readyTimeoutMillis = builder.readyTimeoutMillis;
preAuthorizeServers = builder.preAuthorizeServers;
useLegacyAuthStrategy = builder.useLegacyAuthStrategy;
executorService = scheduledExecutorPool.getObject();
offloadExecutor = offloadExecutorPool.getObject();
}
@Override
public BinderClientTransport newClientTransport(
SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
if (closed) {
throw new IllegalStateException("The transport factory is closed.");
}
return new BinderClientTransport(this, (AndroidComponentAddress) addr, options);
}
@Override
public ScheduledExecutorService getScheduledExecutorService() {
return executorService;
}
@Override
public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
return null;
}
@Override
public void close() {
if (closed) {
return;View on GitHub (pinned to 64daddc1f3)
Solutions
- Do not start new RPCs after calling shutdown()/shutdownNow() on the channel that owns the factory
- Await channel termination (awaitTermination) and recreate a fresh channel if further calls are needed
- Guard application code so lifecycle events (Android onDestroy/rebind) close the channel only when no calls will follow
Example fix
// before channel.shutdown(); stub.call(request); // may hit closed factory // after channel.shutdown(); channel.awaitTermination(5, TimeUnit.SECONDS); channel = BinderChannelBuilder.forAddress(addr, context).build(); stub.call(request);
Defensive patterns
Strategy: try-catch
Validate before calling
if (channel.isShutdown() || channel.isTerminated()) {
channel = BinderChannelBuilder.forAddress(addr, context).build();
}
stub.call(request); Type guard
static boolean isUsable(ManagedChannel ch) {
return ch != null && !ch.isShutdown() && !ch.isTerminated();
} Try / catch
try {
stub.call(request);
} catch (IllegalStateException e) {
if (e.getMessage() != null && e.getMessage().contains("transport factory is closed")) {
channel = BinderChannelBuilder.forAddress(addr, context).build();
stub = newStub(channel);
} else { throw e; }
} Prevention
- Never issue RPCs after channel shutdown(); recreate the channel if needed
- Tie channel close() to Android component lifecycle with no in-flight calls
- Use awaitTermination before reusing or discarding channel references
- Keep a single channel owner per process to avoid racing shutdowns
When it happens
Trigger: Calling newClientTransport(...) on a BinderClientTransportFactory after close(); typically triggered when the channel attempts reconnect/transport creation after the channel (and its factory) has been closed.
Common situations: Using a ManagedChannel after shutdown(); concurrent shutdown while a call is still being placed; keeping a reference to a closed channel in a long-lived component (e.g. an Android Service recreated/rebound).
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
- call forAddress(AndroidComponentAddress, Context) instead
- call forAddress() instead
- TLS not supported in BinderServer
- The transport factory is closed.
- Unable to load OkHttpChannelProvider
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/ec982dfefed27d13.
Report an issue: GitHub.