grpc/grpc-go · error
rls: failed to parse lookup_service_timeout in route lookup…
Error message
rls: failed to parse lookup_service_timeout in route lookup config %+v: %v
What it means
Emitted from parseRLSProto (config.go:215) when convertDuration fails for RouteLookupConfig.lookup_service_timeout. convertDuration calls durationpb.Duration.CheckValid, which rejects out-of-range seconds/nanos (approx |seconds| < ~3e11 and nanos in [0, 1e9)). This is the RLS RPC deadline; a zero value is allowed and defaults to 10s.
Solutions
- Set lookup_service_timeout to a well-formed duration such as {"seconds": 10} or omit it to take the 10s default.
- Audit the control-plane code that serializes the duration to confirm it uses durationpb.New with a time.Duration.
- Cap any user-supplied value to a sane range before serialization.
Example fix
// before
"lookupServiceTimeout": { "seconds": 9999999999999999999, "nanos": 0 }
// after
"lookupServiceTimeout": { "seconds": 10 } Defensive patterns
Strategy: validation
Validate before calling
func validateLookupTimeout(d *durationpb.Duration) error {
if d == nil {
return nil // zero is valid; defaults to 10s
}
if _, err := d.AsDuration(), d.CheckValid(); err != nil {
return fmt.Errorf("lookup_service_timeout is invalid: %w", err)
}
return nil
} Type guard
func isValidLookupTimeout(d *durationpb.Duration) bool {
if d == nil {
return true
}
return d.CheckValid() == nil
} Prevention
- Produce durations with durationpb.New(time.Duration), never by hand-setting fields.
- Treat omitted lookup_service_timeout as acceptable (10s default applies).
- Range-check user-supplied durations before serialization on the control plane.
When it happens
Trigger: lookup_service_timeout is a google.protobuf.Duration whose seconds field exceeds the valid range or whose nanos field is >= 1e9 or negative-and-out-of-range.
Common situations: A control-plane bug emitting INT64 seconds (e.g. milliseconds passed as seconds); a hand-written proto duration with nanos=2000000000; schema-mismatch where a plain JSON number is fed where a duration object is expected (that typically fails earlier at protojson).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- rls: failed to parse max_age in route lookup config %+v
- rls: failed to parse staleAge in route lookup config %+v
- rls: bad RouteLookupConfig proto %+v
- rls: stale_age is set, but max_age is not in route lookup…
- OutlierDetectionLoadBalancingConfig.base_ejection_time =
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/44670102dbd81ccd.
Report an issue: GitHub.
Appendix: source
Thrown at balancer/rls/config.go:215
}
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:
// - if `max_age` > 5m, it should be set to 5 minutes
// only if stale age is not set
// - if `stale_age` > `max_age`, ignore it
// - if `stale_age` is set, then `max_age` must also be set
maxAgeSet := false
maxAge, err := convertDuration(rlsProto.GetMaxAge())
if err != nil {
return nil, fmt.Errorf("rls: failed to parse max_age in route lookup config %+v: %v", rlsProto, err)
}
if maxAge == 0 {
maxAge = maxMaxAge
} else {View on GitHub (pinned to 0c51461d27)