grpc/grpc-go · error
last connection error: %v; last resolver error: %v
Error message
last connection error: %v; last resolver error: %v
What it means
Produced by baseBalancer.mergeErrors when TransientFailure has BOTH a connection error and a resolver error. This combined form is the most diagnostic: it tells you both layers are failing (e.g. the resolver returned a partial/bad update AND the existing SubConns can't connect). Both underlying errors are concatenated so you can triage in one message.
Source
Thrown at balancer/base/balancer.go:158
}
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 {
readySCs[sc] = SubConnInfo{Address: addr}
}View on GitHub (pinned to 03255a9237)
Solutions
- Read BOTH wrapped values: the connection error first, the resolver error second.
- Fix whichever is the root cause (often the resolver error clears first once discovery heals).
- Capture a channelz snapshot to see per-SubConn state and the resolver's last error.
- Apply a client RetryPolicy so the RPC survives the transient dual failure.
Example fix
// Diagnose from the message itself, e.g.: // 'last connection error: dial tcp: connect: connection refused; last resolver error: produced zero addresses' // -> backends down AND resolver returning nothing. Fix service discovery first, then backend health.
Defensive patterns
Strategy: retry
Validate before calling
// Combine resolver + dial pre-flight checks from errors 92 and 93.
func preflight(target, dialAddr string) error {
if err := checkResolves(target); err != nil { return err }
return checkDial(dialAddr, 2*time.Second)
} Try / catch
// Both layers failing usually means a broader outage; retry with backoff and
// surface a clear error to the caller after exhausting retries.
policy := retryPolicy(codes.Unavailable, 5, 1*time.Second)
return withRetry(ctx, policy, func() error { return stub.Do(ctx, req) }) Prevention
- Monitor resolver AND backend health independently to localize the fault.
- Use channelz snapshots during dual failures to capture both errors.
- Configure RetryPolicy and graceful shutdown windows during deploys.
- Keep service discovery and backend health checks tightly coupled.
When it happens
Trigger: The balancer is in TransientFailure, connErr != nil and resolverErr != nil simultaneously. mergeErrors reaches the final return line.
Common situations: Resolver returns an error (DNS hiccup, xDS NACK) at the same time as existing backends are unreachable; a rolling deploy where the resolver hasn't refreshed and the old endpoints are dying; misconfigured custom resolver returning errors while TLS is also broken.
Related errors
- last resolver error: %v
- last connection 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/b83d3177da1e6052.
Report an issue: GitHub.