grpc/grpc-go · error
rls: failed to parse max_age in route lookup config %+v
Error message
rls: failed to parse max_age in route lookup config %+v: %v
What it means
Emitted from parseRLSProto (config.go:229) when convertDuration fails for RouteLookupConfig.max_age (durationpb.CheckValid rejects out-of-range values). max_age is the upper bound on how long a cached RLS result is used; a zero/absent value is allowed and defaults to 5 minutes (and is capped at 5 minutes anyway).
Solutions
- Provide max_age as a valid duration, e.g. {"seconds": 300}, or omit it to use the 5-minute default.
- Fix the producer to use durationpb.New(time.Duration) rather than hand-building fields.
- If you need longer caching, note the client caps max_age at 5 minutes (maxMaxAge), so set stale_age instead for background refresh.
Example fix
// before
"maxAge": { "seconds": 3153600000, "nanos": -5 }
// after
"maxAge": { "seconds": 300 } Defensive patterns
Strategy: validation
Validate before calling
func validateMaxAge(d *durationpb.Duration) error {
if d == nil {
return nil // zero is valid; defaults to 5m
}
if d.CheckValid() != nil {
return fmt.Errorf("max_age is invalid: %w", d.CheckValid())
}
return nil
} Type guard
func isValidMaxAge(d *durationpb.Duration) bool {
return d == nil || d.CheckValid() == nil
} Prevention
- Serialize max_age via durationpb.New(time.Duration).
- Remember the client caps effective max_age at 5 minutes regardless.
- Validate durations at the producer boundary to catch overflow before it reaches clients.
When it happens
Trigger: max_age seconds/nanos out of durationpb's representable range. Zero is valid (means default), so this fires only for genuinely malformed durations.
Common situations: Control plane serializing milliseconds into the seconds field overflowing the range; malformed duration produced by a non-proto serializer.
Understand the failure class
- 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 lookup_service_timeout in route lookup…
- 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/a6bd2e4f62b0ff2a.
Report an issue: GitHub.
Appendix: source
Thrown at balancer/rls/config.go:229
}
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 {
maxAgeSet = true
}
staleAgeSet := false
staleAge, err := convertDuration(rlsProto.GetStaleAge())
if err != nil {
return nil, fmt.Errorf("rls: failed to parse staleAge in route lookup config %+v: %v", rlsProto, err)
}
if staleAge == 0 {
staleAge = maxMaxAge
} else {
staleAgeSet = true
}
View on GitHub (pinned to 0c51461d27)