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

  1. 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.
  2. Before RPC, gate on connectivity: wait for cc.WaitForStateChange(ctx, connectivity.Connecting) / GetState()==connectivity.Ready.
  3. 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.
  4. 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

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


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/3ed93ab1495e18cd. Report an issue: GitHub.