grpc/grpc-go · error
rls: failed to parse staleAge in route lookup config %+v
Error message
rls: failed to parse staleAge in route lookup config %+v: %v
What it means
Emitted from parseRLSProto (config.go:240) when convertDuration fails for RouteLookupConfig.stale_age. stale_age is the age after which a cached entry is refreshed in the background while still serving stale data; zero is valid (defaults to maxMaxAge). Only an out-of-range durationpb value triggers this.
Solutions
- Provide stale_age as a valid duration smaller than max_age, e.g. {"seconds": 240}, or omit it.
- Ensure the producer uses durationpb.New; validate that 0 < staleAge <= maxAge semantically after fixing the encoding.
- Remember the client caps stale_age at 5 minutes and clamps it to max_age if larger.
Example fix
// before
"staleAge": { "seconds": 0, "nanos": 5000000000 }
// after
"staleAge": { "seconds": 240 } Defensive patterns
Strategy: validation
Validate before calling
func validateStaleAge(d *durationpb.Duration) error {
if d == nil {
return nil // zero is valid
}
if d.CheckValid() != nil {
return fmt.Errorf("stale_age is invalid: %w", d.CheckValid())
}
return nil
} Type guard
func isValidStaleAge(d *durationpb.Duration) bool {
return d == nil || d.CheckValid() == nil
} Prevention
- Always set max_age when you set stale_age, with stale_age <= max_age.
- Use durationpb.New to avoid nanos/seconds range errors.
- Note the client clamps stale_age to max_age and to the 5-minute cap.
When it happens
Trigger: stale_age seconds/nanos out of durationpb's representable range.
Common situations: Same as max_age: producer writing milliseconds into seconds, or hand-built nanos out of [0, 1e9).
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 max_age 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/8436654fa36d9498.
Report an issue: GitHub.
Appendix: source
Thrown at balancer/rls/config.go:240
// - 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
}
if staleAgeSet && !maxAgeSet {
return nil, fmt.Errorf("rls: stale_age is set, but max_age is not in route lookup config %+v", rlsProto)
}
if staleAge > maxMaxAge {
staleAge = maxMaxAge
}
if !staleAgeSet && maxAge > maxMaxAge {
maxAge = maxMaxAge
}
if staleAge > maxAge {
staleAge = maxAgeView on GitHub (pinned to 0c51461d27)