grpc/grpc-go · error
rls: empty lookup_service in route lookup config %+v
Error message
rls: empty lookup_service in route lookup config %+v
What it means
Returned by parseRLSProto (config.go:194-197) when RouteLookupConfig.lookup_service is empty. lookup_service is the RLS server address the policy dials to resolve targets; an empty value leaves RLS unable to function, so it is mandatory per the validation contract documented above ParseConfig.
Source
Thrown at balancer/rls/config.go:196
return nil, err
}
lbCfg.childPolicyName = name
lbCfg.childPolicyConfig = config
lbCfg.childPolicyTargetField = cfgJSON.ChildPolicyConfigTargetFieldName
return lbCfg, nil
}
func parseRLSProto(rlsProto *rlspb.RouteLookupConfig) (*lbConfig, error) {
// Validations specified on the `grpc_keybuilders` field are performed here.
kbMap, err := keys.MakeBuilderMap(rlsProto)
if err != nil {
return nil, err
}
// `lookup_service` field must be set and must parse as a target URI.
lookupService := rlsProto.GetLookupService()
if lookupService == "" {
return nil, fmt.Errorf("rls: empty lookup_service in route lookup config %+v", rlsProto)
}
parsedTarget, err := url.Parse(lookupService)
if err != nil {
// url.Parse() fails if scheme is missing. Retry with default scheme.
parsedTarget, err = url.Parse(resolver.GetDefaultScheme() + ":///" + lookupService)
if err != nil {
return nil, fmt.Errorf("rls: invalid target URI in lookup_service %s", lookupService)
}
}
if parsedTarget.Scheme == "" {
parsedTarget.Scheme = resolver.GetDefaultScheme()
}
if resolver.Get(parsedTarget.Scheme) == nil {
return nil, fmt.Errorf("rls: unregistered scheme in lookup_service %s", lookupService)
}
lookupServiceTimeout, err := convertDuration(rlsProto.GetLookupServiceTimeout())
if err != nil {View on GitHub (pinned to 03255a9237)
Solutions
- Set lookup_service to the URI/authority of your RLS server (e.g. "rls.example:443" or "dns:///rls.example:443").
- Confirm the control plane populates RouteLookupConfig.lookup_service from the xDS RLS Cluster resource.
Example fix
// before
rlc := &rlspb.RouteLookupConfig{ /* lookup_service missing */ }
// after
rlc := &rlspb.RouteLookupConfig{ LookupService: "dns:///rls.example:443" } Defensive patterns
Strategy: validation
Validate before calling
func validateLookupService(rlc *rlspb.RouteLookupConfig) error {
if rlc.GetLookupService() == "" {
return errors.New("lookup_service must be set")
}
return nil
} Type guard
func hasLookupService(rlc *rlspb.RouteLookupConfig) bool { return rlc.GetLookupService() != "" } Prevention
- Always populate lookup_service with the RLS server address.
- Fail fast in config construction if the RLS address is unknown.
When it happens
Trigger: The RouteLookupConfig proto has lookup_service unset/empty (default proto value).
Common situations: Control plane emitting an RLS config before the RLS server address is known. Hand-authored proto with lookup_service omitted.
Related errors
- rls: json unmarshal failed for service config %+v: %v
- rls: bad RouteLookupConfig proto %+v: %v
- rls: bad control channel service config %q: %v
- rls: childPolicyConfigTargetFieldName field is not set in se
- rls: invalid target URI in lookup_service %s
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/2e2a0d8c557295c4.
Report an issue: GitHub.