{"record":{"id":"3ed93ab1495e18cd","repo":"grpc/grpc-go","slug":"no-subconn-is-available","errorCode":null,"errorMessage":"no SubConn is available","messagePattern":"no SubConn is available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"balancer/balancer.go","lineNumber":268,"sourceCode":"\t// Err is the rpc error the RPC finished with. It could be nil.\n\tErr error\n\t// Trailer contains the metadata from the RPC's trailer, if present.\n\tTrailer metadata.MD\n\t// BytesSent indicates if any bytes have been sent to the server.\n\tBytesSent bool\n\t// BytesReceived indicates if any byte has been received from the server.\n\tBytesReceived bool\n\t// ServerLoad is the load received from server. It's usually sent as part of\n\t// trailing metadata.\n\t//\n\t// The only supported type now is *orca_v3.LoadReport.\n\tServerLoad any\n}\n\nvar (\n\t// ErrNoSubConnAvailable indicates no SubConn is available for pick().\n\t// gRPC will block the RPC until a new picker is available via UpdateState().\n\tErrNoSubConnAvailable = errors.New(\"no SubConn is available\")\n\t// ErrTransientFailure indicates all SubConns are in TransientFailure.\n\t// WaitForReady RPCs will block, non-WaitForReady RPCs will fail.\n\t//\n\t// Deprecated: return an appropriate error based on the last resolution or\n\t// connection attempt instead.  The behavior is the same for any non-gRPC\n\t// status error.\n\tErrTransientFailure = errors.New(\"all SubConns are in TransientFailure\")\n)\n\n// PickResult contains information related to a connection chosen for an RPC.\ntype PickResult struct {\n\t// SubConn is the connection to use for this pick, if its state is Ready.\n\t// If the state is not Ready, gRPC will block the RPC until a new Picker is\n\t// provided by the balancer (using ClientConn.UpdateState).  The SubConn\n\t// must be one returned by ClientConn.NewSubConn.\n\tSubConn SubConn\n\n\t// Done is called when the RPC is completed.  If the SubConn is not ready,","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/balancer.go#L250-L286","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresp, err := client.SayHello(ctx, &pb.HelloRequest{Name: \"x\"})\n// err may surface Unavailable wrapping 'no SubConn is available'\n\n// after\nresp, err := client.SayHello(ctx, &pb.HelloRequest{Name: \"x\"}, grpc.WaitForReady(true))","handlingStrategy":"retry","validationCode":"// Issue RPCs only once the channel has at least one READY subchannel\nfor {\n    s := cc.GetState()\n    if s == connectivity.Ready {\n        break\n    }\n    if !cc.WaitForStateChange(ctx, s) { // ctx done -> give up\n        return ctx.Err()\n    }\n}","typeGuard":null,"tryCatchPattern":"// In a custom picker: distinguish the sentinel from real failures\npr, err := picker.Pick(info)\nswitch {\ncase err == nil:\n    use(pr.SubConn)\ncase errors.Is(err, balancer.ErrNoSubConnAvailable):\n    // expected transient wait; do not surface as terminal error\ncase status.Code(err) != codes.Unknown:\n    // real gRPC status\n}","preventionTips":["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."],"tags":["balancer","load-balancing","connection","grpc-go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}