{"record":{"id":"44670102dbd81ccd","repo":"grpc/grpc-go","slug":"rls-failed-to-parse-lookup-service-timeout-in-rou","errorCode":null,"errorMessage":"rls: failed to parse lookup_service_timeout in route lookup config %+v: %v","messagePattern":"rls: failed to parse lookup_service_timeout in route lookup config %\\+v: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/config.go","lineNumber":215,"sourceCode":"\t}\n\tparsedTarget, err := url.Parse(lookupService)\n\tif err != nil {\n\t\t// url.Parse() fails if scheme is missing. Retry with default scheme.\n\t\tparsedTarget, err = url.Parse(resolver.GetDefaultScheme() + \":///\" + lookupService)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"rls: invalid target URI in lookup_service %s\", lookupService)\n\t\t}\n\t}\n\tif parsedTarget.Scheme == \"\" {\n\t\tparsedTarget.Scheme = resolver.GetDefaultScheme()\n\t}\n\tif resolver.Get(parsedTarget.Scheme) == nil {\n\t\treturn nil, fmt.Errorf(\"rls: unregistered scheme in lookup_service %s\", lookupService)\n\t}\n\n\tlookupServiceTimeout, err := convertDuration(rlsProto.GetLookupServiceTimeout())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"rls: failed to parse lookup_service_timeout in route lookup config %+v: %v\", rlsProto, err)\n\t}\n\tif lookupServiceTimeout == 0 {\n\t\tlookupServiceTimeout = defaultLookupServiceTimeout\n\t}\n\n\t// Validations performed here:\n\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 {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/rls/config.go#L197-L233","documentation":"Returned by parseRLSProto (config.go:213-216) when convertDuration fails on RouteLookupConfig.lookup_service_timeout. convertDuration calls durationpb.Duration.CheckValid(), which rejects out-of-range durations (seconds/nanos out of int64 range, nanos not in [-999999999,999999999]). So the proto Duration is structurally invalid. The %+v is the whole rlsProto.","triggerScenarios":"lookup_service_timeout has seconds beyond int64 range or malformed nanos, e.g. from a control plane that computed the duration incorrectly.","commonSituations":"Control plane arithmetic overflow when building the duration. Manually crafting a Duration proto with garbage values. Edge value of 0 is fine (falls back to 10s default); only out-of-range values trigger this.","solutions":["Set lookup_service_timeout to a normal, in-range value (e.g. 10s; the default if unset). Typical values are 1-10 seconds.","If generating the Duration proto, use durationpb.New(time.Duration) which always produces a valid value.","Verify the control plane's computation of the timeout field."],"exampleFix":"// before: hand-built out-of-range duration\nrlc.LookupServiceTimeout = &durationpb.Duration{ Seconds: math.MaxInt64, Nanos: 5000000000 }\n\n// after\nrlc.LookupServiceTimeout = durationpb.New(10 * time.Second)","handlingStrategy":"validation","validationCode":"import \"google.golang.org/protobuf/types/known/durationpb\"\n\nfunc validateLookupServiceTimeout(d *durationpb.Duration) error {\n    if d == nil { return nil }\n    return d.CheckValid()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build durations with durationpb.New(time.Duration), never by hand.","Use a small timeout (e.g. 10s); 0 falls back to the 10s default."],"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"}