{"record":{"id":"8436654fa36d9498","repo":"grpc/grpc-go","slug":"rls-failed-to-parse-staleage-in-route-lookup-conf","errorCode":null,"errorMessage":"rls: failed to parse staleAge in route lookup config %+v: %v","messagePattern":"rls: failed to parse staleAge in route lookup config %\\+v: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/config.go","lineNumber":240,"sourceCode":"\t// - if `max_age` > 5m, it should be set to 5 minutes\n\t//   only if stale age is not set\n\t// - if `stale_age` > `max_age`, ignore it\n\t// - if `stale_age` is set, then `max_age` must also be set\n\tmaxAgeSet := false\n\tmaxAge, err := convertDuration(rlsProto.GetMaxAge())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rls: failed to parse max_age in route lookup config %+v: %v\", rlsProto, err)\n\t}\n\tif maxAge == 0 {\n\t\tmaxAge = maxMaxAge\n\t} else {\n\t\tmaxAgeSet = true\n\t}\n\n\tstaleAgeSet := false\n\tstaleAge, err := convertDuration(rlsProto.GetStaleAge())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rls: failed to parse staleAge in route lookup config %+v: %v\", rlsProto, err)\n\t}\n\tif staleAge == 0 {\n\t\tstaleAge = maxMaxAge\n\t} else {\n\t\tstaleAgeSet = true\n\t}\n\n\tif staleAgeSet && !maxAgeSet {\n\t\treturn nil, fmt.Errorf(\"rls: stale_age is set, but max_age is not in route lookup config %+v\", rlsProto)\n\t}\n\tif staleAge > maxMaxAge {\n\t\tstaleAge = maxMaxAge\n\t}\n\tif !staleAgeSet && maxAge > maxMaxAge {\n\t\tmaxAge = maxMaxAge\n\t}\n\tif staleAge > maxAge {\n\t\tstaleAge = maxAge","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/rls/config.go#L222-L258","documentation":"Returned by parseRLSProto (config.go:238-241) when convertDuration fails on RouteLookupConfig.stale_age, i.e. the stale_age Duration proto is structurally invalid (durationpb.CheckValid rejects out-of-range seconds/nanos). The policy also enforces that if stale_age is set then max_age must be set, and caps stale_age at min(max_age, 5m), but those are separate checks; this one is purely the Duration's structural validity.","triggerScenarios":"stale_age Duration proto has out-of-range seconds or nanos. A value of 0 is valid (defaults to 5m).","commonSituations":"Same as max_age: control-plane overflow or hand-crafted invalid Duration. Note: if you set stale_age you must also set max_age or you'll get the follow-on \"stale_age is set, but max_age is not\" error at config.go:248-250.","solutions":["Set stale_age to a valid in-range duration (typically <= max_age and <= 5m).","Build it with durationpb.New(time.Duration).","Remember to also set max_age when you set stale_age."],"exampleFix":"// before\nrlc.StaleAge = &durationpb.Duration{ Seconds: 99999999999, Nanos: 0 } // out of range\n\n// after\nrlc.MaxAge = durationpb.New(5 * time.Minute)\nrlc.StaleAge = durationpb.New(1 * time.Minute)","handlingStrategy":"validation","validationCode":"func validateStaleAge(staleAge, maxAge *durationpb.Duration) error {\n    if staleAge == nil { return nil }\n    if err := staleAge.CheckValid(); err != nil { return err }\n    if maxAge == nil || (maxAge.AsDuration() == 0 && maxAge.Seconds == 0 && maxAge.Nanos == 0) {\n        // will be caught later, but warn early\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build stale_age with durationpb.New and keep it <= max_age.","Always set max_age whenever you set stale_age (else the follow-on error fires).","Both are capped at 5 minutes."],"tags":["go","grpc","load-balancing","rls","config","duration","protobuf"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}