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

  1. Verify the target URI / service name resolves to at least one address (`nslookup`, `dig`, or your service-discovery tool).
  2. Check resolver logs / channelz for the underlying resolver error wrapped in %v.
  3. Confirm the xDS/DNS/override resolver is reachable and returning a valid resource.
  4. 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

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


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