grpc/grpc-go · error

rls: invalid target URI in lookup_service %s

Error message

rls: invalid target URI in lookup_service %s

What it means

Returned by parseRLSProto (config.go:198-204) when lookup_service cannot be parsed as a target URI even after prefixing the default scheme (resolver.GetDefaultScheme() + ":///"). url.Parse failed both on the raw value and on the default-scheme-prefixed value, so the string is not a usable dial target.

Source

Thrown at balancer/rls/config.go:203

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 {
		return nil, fmt.Errorf("rls: failed to parse lookup_service_timeout in route lookup config %+v: %v", rlsProto, err)
	}
	if lookupServiceTimeout == 0 {
		lookupServiceTimeout = defaultLookupServiceTimeout
	}

	// Validations performed here:

View on GitHub (pinned to 03255a9237)

Solutions

  1. Provide a clean host[:port] or a fully-qualified target URI for lookup_service (e.g. "dns:///rls.example:443").
  2. Sanitize the address string and re-test with url.Parse before shipping the config.
  3. Check the xDS control plane's lookup_service field for corruption.

Example fix

// before
rlc.LookupService = "rls example:443" // space makes it un-parseable

// after
rlc.LookupService = "dns:///rls.example:443"
Defensive patterns

Strategy: validation

Validate before calling

import "net/url"
import "google.golang.org/grpc/resolver"

func validateLookupServiceURI(svc string) error {
    t, err := url.Parse(svc)
    if err != nil {
        t, err = url.Parse(resolver.GetDefaultScheme() + ":///" + svc)
        if err != nil {
            return fmt.Errorf("invalid target URI %s", svc)
        }
    }
    _ = t
    return nil
}

Prevention

When it happens

Trigger: lookup_service contains characters that make it an invalid URI even with a scheme prepended (control characters, malformed escaping). The first url.Parse fails (typically because the scheme is missing), and the retry with the default scheme also fails.

Common situations: Typo or stray characters in the RLS server address. Pasting an address with embedded spaces or unescaped special characters. Control-plane templating bug injecting bad bytes.

Related errors


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