{"record":{"id":"953c80d46d00243b","repo":"grpc/grpc-go","slug":"transport-timeout-unit-is-not-recognized-q","errorCode":null,"errorMessage":"transport: timeout unit is not recognized: %q","messagePattern":"transport: timeout unit is not recognized: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/http_util.go","lineNumber":200,"sourceCode":"\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\nconst (\n\tspaceByte   = ' '\n\ttildeByte   = '~'\n\tpercentByte = '%'\n)","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/transport/http_util.go#L182-L218","documentation":"Fires in decodeTimeout (http_util.go:200) when the last character of the grpc-timeout header is not one of the recognized unit suffixes. Valid units are H (hour), M (minute), S (second), m (millisecond), u (microsecond), n (nanosecond); anything else (including uppercase variants of the sub-second units) is rejected.","triggerScenarios":"An inbound grpc-timeout value whose final character is not in {H, M, S, m, u, n}. Examples: \"1000ms\" (wrong: spec uses 'm' for millisecond, not 'ms'), \"5MS\", \"10x\", \"3s\" (lowercase 's' is not valid; seconds use uppercase 'S'). The full offending string is reported.","commonSituations":"A caller formatting the timeout in Go's time.ParseDuration style (\"5s\", \"100ms\") instead of the gRPC HTTP/2 wire format; a non-gRPC client; a proxy that re-encodes the header; documentation misread where 'ms' looks like the millisecond unit.","solutions":["Use the correct single-character unit: H, M, S, m, u, n. Note seconds are 'S' (uppercase) and milliseconds are 'm' (lowercase).","Do not set grpc-timeout by hand; let gRPC derive it from the context deadline.","If you must build it, follow the encoding helper used by the library (encodeTimeout) rather than time.Duration.String()."],"exampleFix":"// before: wrong unit suffixes\n// metadata: {\"grpc-timeout\": \"5s\"}     // lowercase 's' invalid\n// metadata: {\"grpc-timeout\": \"1000ms\"}  // 'ms' invalid\n\n// after\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\ndefer cancel()\nclient.Method(ctx, req)   // emits \"5S\" automatically","handlingStrategy":"validation","validationCode":"var validTimeoutUnits = map[byte]bool{'H': true, 'M': true, 'S': true, 'm': true, 'u': true, 'n': true}\n\nfunc validTimeoutUnit(s string) bool {\n    return len(s) >= 2 && validTimeoutUnits[s[len(s)-1]]\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember seconds='S' uppercase, milliseconds='m' lowercase; no multi-char units like 'ms'.","Avoid setting grpc-timeout by hand; use context deadlines."],"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"}