{"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/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/base/balancer.go#L135-L171","documentation":"Returned by baseBalancer.mergeErrors (balancer.go:153) when the balancer is in TransientFailure and there is no connection error (connErr == nil) but a resolver error exists; the resolver error is wrapped as 'last resolver error: <err>'. This becomes the error handed to RPC clients via NewErrPicker (line 167) when the picker is invoked, so each RPC fails with this message until the resolver recovers.","triggerScenarios":"The name resolver produced an error (or produced zero addresses, which the base balancer reports via b.ResolverError(errors.New(\"produced zero addresses\")) at line 138) while no SubConn had a transport-level error; the channel then enters TRANSIENT_FAILURE and RPCs get this error from the picker.","commonSituations":"DNS name does not resolve / returns NXDOMAIN; xDS resource is absent or ACK/NACK failed; service discovery returns no endpoints (e.g. no healthy pods behind a headless service); xds:/// or dns:/// target with a typo; bootstrap/misconfigured authority.","solutions":["Verify the target authority/name resolves (nslookup/dig for dns:///, or check the xDS resource in the control plane for xds:///).","Ensure at least one backend address is discoverable and the resolver can reach the name server / xDS server.","Let the channel's built-in re-resolution retry; for xDS confirm the LDS/RDS/CDS/EDS resources exist and are accepted.","On the client, use a retry policy / hedging for transient discovery gaps and surface a clear 'no endpoints' cause to operators."],"exampleFix":"// before\nconn, _ := grpc.NewClient(\"xds:///wrong-svc\") // no such resource -> TRANSIENT_FAILURE\n\n// after\nconn, _ := grpc.NewClient(\"xds:///my-ns/my-svc\") // resource exists in control plane\n// and for dns:\nconn, _ := grpc.NewClient(\"dns:///svc.ns.svc.cluster.local:8080\")","handlingStrategy":"retry","validationCode":"// Validate the target resolves before relying on the channel.\n// For dns:///, confirm name resolution:\nfunc resolves(addr string) error {\n    host, _, err := net.SplitHostPort(addr)\n    if err != nil { return err }\n    _, err = net.LookupHost(host)\n    return err\n}","typeGuard":null,"tryCatchPattern":"// RPCs fail with the picker error while the channel is in TRANSIENT_FAILURE.\n// Best practice: let the channel re-resolve and retry the RPC, or use a retry policy.\n_, err := client.Call(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"last resolver error\") {\n        // discovery problem: fix target/DNS/xDS resource; channel will recover\n    }\n}","preventionTips":["Verify the target name resolves (dns) or the xDS resource exists (xds) before relying on the channel.","Use grpc.WithDefaultServiceConfig retry policy to mask transient discovery gaps.","Watch conn.WaitForStateChange(ctx, connectivity.TransientFailure) to react to recovery.","Ensure backends/Endpoints exist and the resolver can reach the name/xDS server."],"tags":["grpc","balancer","resolver","xds","dns","client","network","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}