grpc/grpc-go · warning
no SubConn is available
Error message
no SubConn is available
What it means
Sentinel error (balancer.ErrNoSubConnAvailable) that a Picker returns from Pick() to signal there is currently no usable SubConn. Per the Picker contract in balancer/balancer.go:317-333, gRPC reacts by BLOCKING the RPC until the balancer publishes a new Picker via ClientConn.UpdateState() — it is not itself an RPC failure. It is the standard 'please wait' signal used by LB policies while subchannels are still in CONNECTING/IDLE.
Source
Thrown at balancer/balancer.go:268
// Err is the rpc error the RPC finished with. It could be nil.
Err error
// Trailer contains the metadata from the RPC's trailer, if present.
Trailer metadata.MD
// BytesSent indicates if any bytes have been sent to the server.
BytesSent bool
// BytesReceived indicates if any byte has been received from the server.
BytesReceived bool
// ServerLoad is the load received from server. It's usually sent as part of
// trailing metadata.
//
// The only supported type now is *orca_v3.LoadReport.
ServerLoad any
}
var (
// ErrNoSubConnAvailable indicates no SubConn is available for pick().
// gRPC will block the RPC until a new picker is available via UpdateState().
ErrNoSubConnAvailable = errors.New("no SubConn is available")
// ErrTransientFailure indicates all SubConns are in TransientFailure.
// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
//
// Deprecated: return an appropriate error based on the last resolution or
// connection attempt instead. The behavior is the same for any non-gRPC
// status error.
ErrTransientFailure = errors.New("all SubConns are in TransientFailure")
)
// PickResult contains information related to a connection chosen for an RPC.
type PickResult struct {
// SubConn is the connection to use for this pick, if its state is Ready.
// If the state is not Ready, gRPC will block the RPC until a new Picker is
// provided by the balancer (using ClientConn.UpdateState). The SubConn
// must be one returned by ClientConn.NewSubConn.
SubConn SubConn
// Done is called when the RPC is completed. If the SubConn is not ready,View on GitHub (pinned to 03255a9237)
Solutions
- Issue the RPC with the grpc.WaitForReady(true) CallOption so gRPC blocks until a SubConn becomes READY instead of surfacing the wait as an error.
- Before RPC, gate on connectivity: wait for cc.WaitForStateChange(ctx, connectivity.Connecting) / GetState()==connectivity.Ready.
- If you author a Picker, only return ErrNoSubConnAvailable when an UpdateState is imminent; otherwise surface a descriptive status.Errorf(codes.Unavailable, ...) so non-WaitForReady callers get a clear failure.
- Confirm the resolver actually produced addresses (log resolver.State.Addresses / Endpoints) — an empty resolver result also yields no SubConns.
Example fix
// before
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "x"})
// err may surface Unavailable wrapping 'no SubConn is available'
// after
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "x"}, grpc.WaitForReady(true)) Defensive patterns
Strategy: retry
Validate before calling
// Issue RPCs only once the channel has at least one READY subchannel
for {
s := cc.GetState()
if s == connectivity.Ready {
break
}
if !cc.WaitForStateChange(ctx, s) { // ctx done -> give up
return ctx.Err()
}
} Try / catch
// In a custom picker: distinguish the sentinel from real failures
pr, err := picker.Pick(info)
switch {
case err == nil:
use(pr.SubConn)
case errors.Is(err, balancer.ErrNoSubConnAvailable):
// expected transient wait; do not surface as terminal error
case status.Code(err) != codes.Unknown:
// real gRPC status
} Prevention
- Default to grpc.WaitForReady(true) for non-urgent RPCs so the wait is handled by gRPC.
- Author pickers to return ErrNoSubConnAvailable only when an UpdateState is imminent.
- Gate long-lived logic on cc.GetState() rather than blindly issuing RPCs right after Dial.
When it happens
Trigger: A balancer Picker's Pick() returns balancer.ErrNoSubConnAvailable. This happens in stock policies (e.g. weightedtarget/weightedaggregator/aggregator.go:225 and :270, base round_robin before READY) and in any custom Picker you author. Concretely: an RPC is issued while every SubConn is in CONNECTING/IDLE, or a resolver just delivered addresses that have not connected yet.
Common situations: Calling RPCs immediately after grpc.NewClient/Dial before the first backend reaches READY; a custom LoadBalancer whose Picker forgets to handle the no-ready case; resolver returned addresses but the server is slow/unreachable so subchannels stay CONNECTING; tests that dial a not-yet-started server.
Related errors
- all SubConns are in TransientFailure
- bad resolver state
- no children to pick from
- weighted-target: no targets to pick from
- grpc: the connection is drained
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/3ed93ab1495e18cd.
Report an issue: GitHub.