{"record":{"id":"43e2f015ab3728aa","repo":"grpc/grpc-go","slug":"last-resolver-error-v","errorCode":null,"errorMessage":"last resolver error: %v","messagePattern":"last resolver error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/base/balancer.go","lineNumber":153,"sourceCode":"\t// the overall state turns transient failure, the error message will have\n\t// the zero address information.\n\tif len(s.ResolverState.Addresses) == 0 {\n\t\tb.ResolverError(errors.New(\"produced zero addresses\"))\n\t\treturn balancer.ErrBadResolverState\n\t}\n\n\tb.regeneratePicker()\n\tb.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})\n\treturn nil\n}\n\n// mergeErrors builds an error from the last connection error and the last\n// resolver error.  Must only be called if b.state is TransientFailure.\nfunc (b *baseBalancer) mergeErrors() error {\n\t// connErr must always be non-nil unless there are no SubConns, in which\n\t// case resolverErr must be non-nil.\n\tif b.connErr == nil {\n\t\treturn fmt.Errorf(\"last resolver error: %v\", b.resolverErr)\n\t}\n\tif b.resolverErr == nil {\n\t\treturn fmt.Errorf(\"last connection error: %v\", b.connErr)\n\t}\n\treturn fmt.Errorf(\"last connection error: %v; last resolver error: %v\", b.connErr, b.resolverErr)\n}\n\n// regeneratePicker takes a snapshot of the balancer, and generates a picker\n// from it. The picker is\n//   - errPicker if the balancer is in TransientFailure,\n//   - built by the pickerBuilder with all READY SubConns otherwise.\nfunc (b *baseBalancer) regeneratePicker() {\n\tif b.state == connectivity.TransientFailure {\n\t\tb.picker = NewErrPicker(b.mergeErrors())\n\t\treturn\n\t}\n\treadySCs := make(map[balancer.SubConn]SubConnInfo)\n","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/base/balancer.go#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: target typo -> resolver returns zero addresses\nconn, _ := grpc.NewClient(\"dns:///paymets-svc:443\")   // 'paymets' typo\n\n// after:\nconn, _ := grpc.NewClient(\"dns:///payments-svc:443\")","handlingStrategy":"retry","validationCode":"// Before opening the channel, sanity-check the target resolves to addresses\n// using the same resolver the channel will use.\nfunc checkResolves(target string) error {\n    // for dns resolver targets of the form dns:///host:port\n    host := strings.TrimPrefix(target, \"dns:///\")\n    if _, err := net.LookupHost(strings.Split(host, \":\")[0]); err != nil {\n        return fmt.Errorf(\"pre-flight DNS lookup failed: %v\", err)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// RPCs fail with codes.Unavailable carrying this message; retry idempotent ones.\nfor i := 0; i < maxRetries; i++ {\n    err := stub.Do(ctx, req)\n    if err == nil { break }\n    if status.Code(err) != codes.Unavailable { return err }\n    backoff.Sleep(ctx, i)\n}\n// Prefer grpc's built-in RetryPolicy on the channel for transient-failure retries.","preventionTips":["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."],"tags":["balancer","base","transient-failure","resolver","runtime","network"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}