{"record":{"id":"92cf394d7314dca4","repo":"grpc/grpc-go","slug":"transport-timeout-string-is-too-short-q","errorCode":null,"errorMessage":"transport: timeout string is too short: %q","messagePattern":"transport: timeout string is too short: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/transport/http_util.go","lineNumber":191,"sourceCode":"\tcase minute:\n\t\treturn time.Minute, true\n\tcase second:\n\t\treturn time.Second, true\n\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","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/transport/http_util.go#L173-L209","documentation":"This error occurs in decodeTimeout when the grpc-timeout HTTP/2 header value is shorter than 2 characters. The gRPC timeout format requires at least one digit followed by a unit character (e.g., '1S'), so a 0- or 1-character string cannot be parsed.","triggerScenarios":"An incoming gRPC request (or the client side decoding a server-sent timeout) has a grpc-timeout header with fewer than 2 bytes. This is typically caused by a malformed peer or an intermediary (proxy/LB) corrupting the header.","commonSituations":"A non-gRPC client or buggy proxy sends an empty or truncated grpc-timeout header; interoperability issues with an HTTP/2 implementation that strips the header value; or a load balancer that mangles header values.","solutions":["Inspect the grpc-timeout header on the offending request using network tracing (e.g., Wireshark with HTTP/2 dissection).","Fix or upgrade the client/proxy that produces the truncated header.","If you control the client, ensure context deadlines are set properly so grpc-timeout is well-formed."],"exampleFix":"// before (broken): empty or single-char timeout header sent by client\nmetadata: {\"grpc-timeout\": \"\"}\n\n// after (valid): client sets a proper deadline\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\n// grpc-go encodes this as \"5000m\" (5 seconds in millis)","handlingStrategy":"validation","validationCode":"// Validate a grpc-timeout header value before processing\nfunc validateGrpcTimeout(s string) error {\n    if len(s) < 2 {\n        return fmt.Errorf(\"timeout too short: needs >=1 digit + unit\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// On the server side, malformed grpc-timeout results in a stream abort\n// with codes.Internal. Handle gracefully:\nif status.Code(err) == codes.Internal {\n    if strings.Contains(err.Error(), \"malformed grpc-timeout\") {\n        log.Printf(\"client sent invalid timeout header\")\n    }\n}","preventionTips":["Always set context deadlines on the client side so grpc-timeout is well-formed.","Use a compliant gRPC client library to ensure proper header encoding.","Monitor for protocol-level rejections in server logs to detect buggy clients."],"tags":["transport","http2","timeout","header-parsing","network"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}