{"record":{"id":"14b961b078af0c91","repo":"grpc/grpc-go","slug":"rls-response-s-target-list-does-not-contain-any-en","errorCode":null,"errorMessage":"RLS response's target list does not contain any entries for key %+v","messagePattern":"RLS response's target list does not contain any entries for key %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"balancer/rls/picker.go","lineNumber":320,"sourceCode":"\t// - status is set to error returned from the control channel\n\t// - current backoff state is available in the pending entry\n\t//   - `retries` field is incremented and\n\t//   - backoff state is moved to the data cache\n\t// - backoffTime is set to the time indicated by the backoff state\n\t// - backoffExpirationTime is set to twice the backoff time\n\t// - backoffTimer is set to fire after backoffTime\n\t//\n\t// When a proactive cache refresh fails, this would leave the targets and the\n\t// expiry time from the old entry unchanged. And this mean that the old valid\n\t// entry would be used until expiration, and a new picker would be sent upon\n\t// backoff expiry.\n\tnow := time.Now()\n\n\t// \"An RLS request is considered to have failed if it returns a non-OK\n\t// status or the RLS response's targets list is non-empty.\" - RLS LB Policy\n\t// design.\n\tif len(targets) == 0 && err == nil {\n\t\terr = fmt.Errorf(\"RLS response's target list does not contain any entries for key %+v\", cacheKey)\n\t\t// If err is set, rpc error from the control plane and no control plane\n\t\t// configuration is why no targets were passed into this helper, no need\n\t\t// to specify and tell the user this information.\n\t}\n\tif err != nil {\n\t\tdcEntry.status = err\n\t\tpendingEntry := p.lb.pendingMap[cacheKey]\n\t\tpendingEntry.retries++\n\t\tbackoffTime := pendingEntry.bs.Backoff(pendingEntry.retries)\n\t\tdcEntry.backoffState = pendingEntry\n\t\tdcEntry.backoffTime = now.Add(backoffTime)\n\t\tdcEntry.backoffExpiryTime = now.Add(2 * backoffTime)\n\t\tif dcEntry.backoffState.timer != nil {\n\t\t\tdcEntry.backoffState.timer.Stop()\n\t\t}\n\t\tdcEntry.backoffState.timer = time.AfterFunc(backoffTime, p.lb.sendNewPicker)\n\t\treturn\n\t}","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/rls/picker.go#L302-L338","documentation":"Per gRFC A24, an RLS response is considered failed if it returns OK status but an empty targets list. grpc-go enforces this in handleRouteLookupResponse (picker.go:319-320): a successful RPC with zero targets is converted into this synthetic error, which then drives the backoff state machine. The RPC that triggered the lookup is queued or routed to the default target.","triggerScenarios":"The RLS server returns a RouteLookupResponse with targets=[] and no error. handleRouteLookupResponse synthesizes this error and stores it on the cache entry, scheduling backoff.","commonSituations":"The RLS server has no routing entry for the requested key (misconfigured upstream); the RLS server returns success but its own config is empty; a transient control-plane issue where the lookup matches no targets.","solutions":["Check the RLS server's routing table for the key shown in the error and add a target if one is expected.","Configure a default target in the RLS LB config so RPCs can fall back when RLS returns no targets (useDefaultPickIfPossible at picker.go:231).","Increase RLS cache maxAge/staleAge so transient empty responses do not evict good cached targets too aggressively."],"exampleFix":"// before: RLS config with no default target\ncfg := &rlspb.RouteLookupConfig{ GrpcKeybuilders: ..., LookupService: \"rls.example\" }\n// after: provide a default target in the RLS LB policy child config so picks fall back\nlbCfg := `{\"childPolicy\":[{\"rls_experimental\":{\"lookupService\":\"rls.example\",\"childPolicy\":[{\"pick_first\":{}}],\"defaultTarget\":\"default.backend:443\"}}]}`","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Configure a default target in the RLS LB policy so RPCs still succeed when RLS returns no targets.","Monitor the RLS server's routing table coverage for the keys your clients send.","Watch grpc.rls.request_count metrics for the \"dropped\" dimension to detect empty responses early."],"tags":["go","grpc","rls","control-plane","runtime","fallback"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}