grpc/grpc-go · error
last resolver error: %v
Error message
last resolver error: %v
What it means
Produced by baseBalancer.mergeErrors (called from regeneratePicker) when the base balancer is in TransientFailure, there is no connection error (connErr == nil) but a resolver error exists. This typically means the name resolver returned an error and no SubConn ever got far enough to produce a connection error. The picker wraps this error and returns it on the next Pick, so it surfaces as the RPC's Unavailable error.
Source
Thrown at balancer/base/balancer.go:153
// the overall state turns transient failure, the error message will have
// the zero address information.
if len(s.ResolverState.Addresses) == 0 {
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)
View on GitHub (pinned to 03255a9237)
Solutions
- Verify the target URI / service name resolves to at least one address (`nslookup`, `dig`, or your service-discovery tool).
- Check resolver logs / channelz for the underlying resolver error wrapped in %v.
- Confirm the xDS/DNS/override resolver is reachable and returning a valid resource.
- Retry with backoff — transient resolver outages often self-heal; configure a RetryPolicy on the client.
Example fix
// before: target typo -> resolver returns zero addresses
conn, _ := grpc.NewClient("dns:///paymets-svc:443") // 'paymets' typo
// after:
conn, _ := grpc.NewClient("dns:///payments-svc:443") Defensive patterns
Strategy: retry
Validate before calling
// Before opening the channel, sanity-check the target resolves to addresses
// using the same resolver the channel will use.
func checkResolves(target string) error {
// for dns resolver targets of the form dns:///host:port
host := strings.TrimPrefix(target, "dns:///")
if _, err := net.LookupHost(strings.Split(host, ":")[0]); err != nil {
return fmt.Errorf("pre-flight DNS lookup failed: %v", err)
}
return nil
} Try / catch
// RPCs fail with codes.Unavailable carrying this message; retry idempotent ones.
for i := 0; i < maxRetries; i++ {
err := stub.Do(ctx, req)
if err == nil { break }
if status.Code(err) != codes.Unavailable { return err }
backoff.Sleep(ctx, i)
}
// Prefer grpc's built-in RetryPolicy on the channel for transient-failure retries. Prevention
- Pre-flight resolve the target before opening the channel.
- Configure a client-side RetryPolicy for codes.Unavailable.
- Use channelz to inspect resolver errors at runtime.
- Double-check the target URI spelling and scheme.
When it happens
Trigger: Using a base-balancer-backed policy (round_robin, etc.) where UpdateClientConnState's ResolverState is bad (e.g. zero addresses) and no SubConn connection attempt has yet failed. mergeErrors hits the `b.connErr == nil` branch.
Common situations: DNS returns no records for the target; an xDS resolver NACKed the resource; the resolver service is unreachable; the target URI is wrong (typo in service name); a custom resolver returns an error.
Related errors
- last connection error: %v; last resolver error: %v
- last connection error: %v
- name resolver error: %v
- all SubConns are in TransientFailure, last connection error:
- all SubConns are in TransientFailure
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/43e2f015ab3728aa.
Report an issue: GitHub.