grpc/grpc-go · error
last connection error: %v
Error message
last connection error: %v
What it means
Produced by baseBalancer.mergeErrors when the balancer is in TransientFailure, a SubConn connection error exists (connErr != nil) but there is no resolver error. This is the classic 'all backends unreachable' case: the resolver gave good addresses but every TCP/TLS handshake failed. The error is wrapped in the picker and returned as the RPC's Unavailable status.
Source
Thrown at balancer/base/balancer.go:156
b.ResolverError(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
b.regeneratePicker()
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
return nil
}
// mergeErrors builds an error from the last connection error and the last
// resolver error. Must only be called if b.state is TransientFailure.
func (b *baseBalancer) mergeErrors() error {
// connErr must always be non-nil unless there are no SubConns, in which
// case resolverErr must be non-nil.
if b.connErr == nil {
return fmt.Errorf("last resolver error: %v", b.resolverErr)
}
if b.resolverErr == nil {
return fmt.Errorf("last connection error: %v", b.connErr)
}
return fmt.Errorf("last connection error: %v; last resolver error: %v", b.connErr, b.resolverErr)
}
// regeneratePicker takes a snapshot of the balancer, and generates a picker
// from it. The picker is
// - errPicker if the balancer is in TransientFailure,
// - built by the pickerBuilder with all READY SubConns otherwise.
func (b *baseBalancer) regeneratePicker() {
if b.state == connectivity.TransientFailure {
b.picker = NewErrPicker(b.mergeErrors())
return
}
readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map.
for addr, sc := range b.subConns.All() {
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {View on GitHub (pinned to 03255a9237)
Solutions
- Read the wrapped %v — it is the underlying connection error (e.g. dial tcp: connection refused, x509 cert error).
- Verify network reachability to the backend address/port from the client host.
- If TLS-related, check cert validity, SANs, and the client's credentials config.
- Restart/health-check the backends and confirm service discovery reflects them as healthy.
Example fix
// before: TLS handshake fails on all backends -> 'last connection error: ... x509: certificate signed by unknown authority'
conn, _ := grpc.NewClient(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))) // no CA
// after:
creds := credentials.NewClientTLSFromCert(caPool, "")
conn, _ := grpc.NewClient(addr, grpc.WithTransportCredentials(creds)) Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: dial the backend address to catch connection-level failures
// before the channel's SubConns all enter TransientFailure.
func checkDial(addr string, timeout time.Duration) error {
conn, err := net.DialTimeout("tcp", addr, timeout)
if err != nil { return err }
conn.Close()
return nil
} Try / catch
// Treat as transient; retry with backoff for idempotent RPCs.
var lastErr error
for i := 0; i < maxRetries; i++ {
lastErr = stub.Do(ctx, req)
if lastErr == nil { return nil }
if status.Code(lastErr) != codes.Unavailable { return lastErr }
select { case <-time.After(backoffFor(i)): case <-ctx.Done(): return ctx.Err() }
}
return lastErr Prevention
- Validate TLS CA/certs before deploying the client.
- Health-check backends and gate on readiness probes.
- Configure keepalive and RetryPolicy to survive transient outages.
- Use channelz to watch per-SubConn connection errors.
When it happens
Trigger: All SubConns under a base-balancer policy (round_robin, weighted_round_robin, etc.) have entered TransientFailure with a non-nil connection error, while the resolver itself succeeded. mergeErrors hits the `b.resolverErr == nil` branch.
Common situations: Backends are down or refusing connections; TLS certificate validation failure on every backend; firewall/security group blocking the gRPC port; wrong port in the resolved address; server crashed and endpoints haven't been re-registered.
Related errors
- last connection error: %v; last resolver error: %v
- last resolver error: %v
- all SubConns are in TransientFailure, last connection error:
- name resolver error: %v
- no SubConn is available
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/1abfb16b7e160a1b.
Report an issue: GitHub.