{"record":{"id":"fd0b0dedf422aa60","repo":"grpc/grpc-go","slug":"out-of-range-q","errorCode":null,"errorMessage":"out of range: %q","messagePattern":"out of range: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/serviceconfig/duration.go","lineNumber":90,"sourceCode":"\t\ts = s[1:]\n\t}\n\tss := strings.SplitN(s[:len(s)-1], \".\", 3)\n\tif len(ss) > 2 {\n\t\treturn fmt.Errorf(\"malformed duration %q: too many decimals\", s)\n\t}\n\t// hasDigits is set if either the whole or fractional part of the number is\n\t// present, since both are optional but one is required.\n\thasDigits := false\n\tvar sec, ns int64\n\tif len(ss[0]) > 0 {\n\t\tvar err error\n\t\tif sec, err = strconv.ParseInt(ss[0], 10, 64); err != nil {\n\t\t\treturn fmt.Errorf(\"malformed duration %q: %v\", s, err)\n\t\t}\n\t\t// Maximum seconds value per the durationpb spec.\n\t\tconst maxProtoSeconds = 315_576_000_000\n\t\tif sec > maxProtoSeconds {\n\t\t\treturn fmt.Errorf(\"out of range: %q\", s)\n\t\t}\n\t\thasDigits = true\n\t}\n\tif len(ss) == 2 && len(ss[1]) > 0 {\n\t\tif len(ss[1]) > 9 {\n\t\t\treturn fmt.Errorf(\"malformed duration %q: too many digits after decimal\", s)\n\t\t}\n\t\tvar err error\n\t\tif ns, err = strconv.ParseInt(ss[1], 10, 64); err != nil {\n\t\t\treturn fmt.Errorf(\"malformed duration %q: %v\", s, err)\n\t\t}\n\t\tfor i := 9; i > len(ss[1]); i-- {\n\t\t\tns *= 10\n\t\t}\n\t\thasDigits = true\n\t}\n\tif !hasDigits {\n\t\treturn fmt.Errorf(\"malformed duration %q: contains no numbers\", s)","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/serviceconfig/duration.go#L72-L108","documentation":"Fires at duration.go:90 when the parsed seconds value exceeds the protobuf Duration spec maximum of 315,576,000,000 seconds (~10,000 years). This is a hard cap defined by the protobuf Duration type, independent of Go's time.Duration range; values above it are rejected even though they might fit in an int64.","triggerScenarios":"A Duration JSON string whose integer seconds part parses successfully but is greater than the const maxProtoSeconds (315576000000). Examples: \"999999999999s\", \"315576000001s\", or a backoff/timeout field populated from a calculation that produced a huge number.","commonSituations":"A retry/backoff multiplier computed from untrusted input that explodes into a huge value; config where MaxBackoff was meant to be in milliseconds but written as seconds (\"1000000000000s\"); copy-paste of a nanosecond quantity into a seconds field.","solutions":["Read the offending %q value and compare its seconds part against 315,576,000,000.","Find the field (retry MaxBackoff, timeout, etc.) that produced the value and cap or correct it.","If the number was meant to be a different unit, re-express it correctly (e.g. milliseconds -> seconds).","Add a unit test that asserts your config-generation code never emits seconds above the protobuf limit."],"exampleFix":"// before\n// \"maxBackoff\": \"1000000000000s\"   // 1e12 s -> out of range\n\n// after\n// \"maxBackoff\": \"600s\"             // 10 minutes","handlingStrategy":"validation","validationCode":"const maxProtoSeconds = 315_576_000_000\n\nfunc assertDurationInRange(d time.Duration) error {\n    if sec := int64(d / time.Second); sec > maxProtoSeconds {\n        return fmt.Errorf(\"duration %s exceeds protobuf max %ds\", d, maxProtoSeconds)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := json.Unmarshal(rawConfig, &cfg); err != nil {\n    if strings.Contains(err.Error(), \"out of range\") {\n        // a Duration field exceeds the ~10000-year protobuf cap\n    }\n    return err\n}","preventionTips":["Clamp computed backoff/timeout values to <= 315576000000 seconds before serializing.","Unit-test config generation with extreme inputs to catch overflow.","Distinguish seconds from milliseconds when authoring config."],"tags":["config","duration","service-config","range","protobuf"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}