grpc/grpc-go · error
all SubConns are in TransientFailure
Error message
all SubConns are in TransientFailure
What it means
Deprecated sentinel (balancer.ErrTransientFailure) a Picker returns when every SubConn is in TransientFailure. Per balancer/balancer.go:269-275 and :330-332, WaitForReady RPCs block while non-WaitForReady RPCs are terminated with status Unavailable carrying this message. The doc comment explicitly tells balancer authors to instead return an appropriate error based on the last resolution/connection attempt; behavior for any non-gRPC-status error is identical, so the sentinel is redundant.
Source
Thrown at balancer/balancer.go:275
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,
// this will be called with a nil parameter. If the SubConn is not a valid
// type, Done may not be called. May be nil if the balancer does not wish
// to be notified when the RPC completes.
Done func(DoneInfo)
// Metadata provides a way for LB policies to inject arbitrary per-call
// metadata. Any metadata returned here will be merged with existingView on GitHub (pinned to 03255a9237)
Solutions
- Inspect cc.connectionError() / the last subchannel error to find the real cause (TLS, DNS, refused) — the TransientFailure message itself is not diagnostic.
- Fix the root backend/network issue so at least one SubConn reaches READY.
- If you maintain a Picker, stop returning ErrTransientFailure; wrap the most recent connection attempt error (e.g. status.Errorf(codes.Unavailable, '%v', lastErr)).
- Use grpc.WaitForReady(true) with a deadline if you want RPCs to ride through transient outages.
Example fix
// before (custom picker)
return balancer.PickResult{}, balancer.ErrTransientFailure
// after
return balancer.PickResult{}, status.Errorf(codes.Unavailable, "last conn error: %v", lastConnErr) Defensive patterns
Strategy: retry
Validate before calling
// Treat TransientFailure as recoverable; check real cause
if cc.GetState() == connectivity.TransientFailure {
if e := cc.connectionError(); e != nil { log.Println("root cause:", e) }
} Try / catch
if errors.Is(err, balancer.ErrTransientFailure) || status.Code(err) == codes.Unavailable {
// back off and retry; consult connectionError() for the real cause
} Prevention
- Do not return ErrTransientFailure from custom pickers — wrap the last connection error instead.
- Enable retry policy in the service config for UNAVAILABLE.
- Log cc.connectionError() so TransientFailure is diagnosable.
When it happens
Trigger: A Picker returns balancer.ErrTransientFailure (or any non-status error with the same semantics) after the connectivity evaluator reports TransientFailure for all subchannels — i.e. every backend connection attempt failed (TCP refused, TLS handshake error, auth failure) and the LB policy aggregated to TransientFailure.
Common situations: All backends down or unreachable; TLS trust chain broken so every handshake fails; the wrong target/authority so connections are refused; a custom balancer still using the deprecated sentinel instead of the last connection error.
Related errors
- all SubConns are in TransientFailure, last connection error:
- no SubConn is available
- bad resolver state
- no children to pick from
- weighted-target: no targets to pick from
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/9abc69b90e1b2e60.
Report an issue: GitHub.