{"record":{"id":"b5322968218b0bf0","repo":"grpc/grpc-go","slug":"transport-timeout-string-is-too-long-q","errorCode":null,"errorMessage":"transport: timeout string is too long: %q","messagePattern":"transport: timeout string is too long: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/http_util.go","lineNumber":195,"sourceCode":"\tcase millisecond:\n\t\treturn time.Millisecond, true\n\tcase microsecond:\n\t\treturn time.Microsecond, true\n\tcase nanosecond:\n\t\treturn time.Nanosecond, true\n\tdefault:\n\t}\n\treturn\n}\n\nfunc decodeTimeout(s string) (time.Duration, error) {\n\tsize := len(s)\n\tif size < 2 {\n\t\treturn 0, fmt.Errorf(\"transport: timeout string is too short: %q\", s)\n\t}\n\tif size > 9 {\n\t\t// Spec allows for 8 digits plus the unit.\n\t\treturn 0, fmt.Errorf(\"transport: timeout string is too long: %q\", s)\n\t}\n\tunit := timeoutUnit(s[size-1])\n\td, ok := timeoutUnitToDuration(unit)\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"transport: timeout unit is not recognized: %q\", s)\n\t}\n\tt, err := strconv.ParseUint(s[:size-1], 10, 64)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tconst maxHours = math.MaxInt64 / uint64(time.Hour)\n\tif d == time.Hour && t > maxHours {\n\t\t// This timeout would overflow math.MaxInt64; clamp it.\n\t\treturn time.Duration(math.MaxInt64), nil\n\t}\n\treturn d * time.Duration(t), nil\n}\n","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/transport/http_util.go#L177-L213","documentation":"Fires in decodeTimeout (http_util.go:195) when the grpc-timeout header value is longer than 9 characters. The gRPC spec allows at most 8 decimal digits plus a single unit character, to bound the encoded size; longer values are rejected as malformed even if they would otherwise parse.","triggerScenarios":"An inbound grpc-timeout value of length > 9. Examples: a caller formatting the timeout with more than 8 significant digits (\"123456789S\"), padding with leading zeros (\"0000000010S\"), or appending extra characters; a proxy that mangles the header; a non-conformant client.","commonSituations":"A custom client/proxy that builds the header without honoring the 8-digit limit; values derived from nanosecond timestamps expressed in hours (huge digit counts); header corruption by a middlebox.","solutions":["Shorten the grpc-timeout value to <= 8 digits plus a unit (choose a larger unit if needed, e.g. \"99999999S\" is fine but \"999999999S\" is not).","Prefer letting gRPC encode the deadline from context.WithTimeout rather than setting grpc-timeout manually.","Audit proxies/LBs for header rewriting that appends or pads characters."],"exampleFix":"// before: manual header with too many digits\n// metadata: {\"grpc-timeout\": \"1000000000S\"}   // 10 digits -> too long\n\n// after\nctx, cancel := context.WithTimeout(ctx, 1000*time.Second)\ndefer cancel()\nclient.Method(ctx, req)   // gRPC emits a spec-compliant value","handlingStrategy":"validation","validationCode":"func validTimeoutHeaderLen(s string) bool {\n    return len(s) >= 2 && len(s) <= 9 // digits + unit\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep grpc-timeout to <= 8 digits plus a unit.","Use gRPC's deadline propagation instead of manual headers.","Pick a larger unit when the value has many digits."],"tags":["transport","http2","timeout","metadata","protocol-violation"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}