grpc/grpc-go · error

all SubConns are in TransientFailure, last connection error:

Error message

all SubConns are in TransientFailure, last connection error: %v

What it means

Produced by grpclb's regeneratePicker when the gRPC-LB balancer is in TransientFailure: every SubConn to the backends selected by the remote balancer has failed, so the picker returns this error on every Pick. The wrapped %v is lb.connErr, the last connection error from the failing SubConns. The RPC fails with Unavailable carrying this message.

Source

Thrown at balancer/grpclb/grpclb.go:249

	remoteBalancerConnected bool
	serverListReceived      bool
	inFallback              bool
	// resolvedBackendAddrs is resolvedAddrs minus remote balancers. It's set
	// when resolved address updates are received, and read in the goroutine
	// handling fallback.
	resolvedBackendAddrs []resolver.Address
	connErr              error // the last connection error
}

// regeneratePicker takes a snapshot of the balancer, and generates a picker from
// it. The picker
//   - always returns ErrTransientFailure if the balancer is in TransientFailure,
//   - does two layer roundrobin pick otherwise.
//
// Caller must hold lb.mu.
func (lb *lbBalancer) regeneratePicker(resetDrop bool) {
	if lb.state == connectivity.TransientFailure {
		lb.picker = base.NewErrPicker(fmt.Errorf("all SubConns are in TransientFailure, last connection error: %v", lb.connErr))
		return
	}

	if lb.state == connectivity.Connecting {
		lb.picker = base.NewErrPicker(balancer.ErrNoSubConnAvailable)
		return
	}

	var readySCs []balancer.SubConn
	if lb.usePickFirst {
		for _, sc := range lb.subConns {
			readySCs = append(readySCs, sc)
			break
		}
	} else {
		for _, a := range lb.backendAddrsWithoutMetadata {
			if sc, ok := lb.subConns[a]; ok {
				if st, ok := lb.scStates[sc]; ok && st == connectivity.Ready {

View on GitHub (pinned to 03255a9237)

Solutions

  1. Read the wrapped connection error %v (refused, TLS, timeout) and fix that layer.
  2. Verify the remote load balancer is returning healthy backend addresses (check the balancer's admin/API).
  3. Confirm grpclb fallback is configured if you want to fall back to resolved addresses when the remote list is bad.
  4. Note: grpclb is deprecated in favor of xDS; consider migrating if this is recurring.

Example fix

// before: all backends refused due to wrong port in remote balancer config
// error: 'all SubConns are in TransientFailure, last connection error: dial tcp: connect: connection refused'

// after: correct the backend port in the remote load balancer's server list so SubConns reach READY
Defensive patterns

Strategy: retry

Validate before calling

// grpclb: pre-flight one of the backends from the remote balancer's list
// (or a fallback address) before relying on the channel.
func checkGrpcLbBackend(addr string) error {
    return checkDial(addr, 2*time.Second) // from error 93
}

Try / catch

// All SubConns in TransientFailure -> Unavailable. Retry idempotent RPCs
// and consider switching off grpclb if it recurs (grpclb is deprecated).
for i := 0; i < maxRetries; i++ {
    err := stub.Do(ctx, req)
    if err == nil { return nil }
    if status.Code(err) != codes.Unavailable { return err }
    select { case <-time.After(backoffFor(i)): case <-ctx.Done(): return ctx.Err() }
}

Prevention

When it happens

Trigger: Using the grpclb:// scheme (delegating to a remote load balancer) and all backend SubConns created from the server list are in TransientFailure. regeneratePicker's first branch fires.

Common situations: The remote balancer returned a server list whose backends are all unreachable; TLS/auth to backends is misconfigured; the backends crashed but the remote balancer hasn't updated its list; network partition between client and backend pool.

Related errors


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