grpc/grpc-go · error
transport: timeout string is too short
Error message
transport: timeout string is too short: %q
What it means
Fires in decodeTimeout (http_util.go:191) when the grpc-timeout header value passed to the server has fewer than 2 characters. Per the gRPC over HTTP/2 spec, the timeout is encoded as <digits><unit>, so the shortest valid value is one digit plus a unit (e.g. "1S"). A shorter value cannot contain both a number and a unit and is malformed.
Solutions
- Identify the caller setting the grpc-timeout header and fix it to the spec format: integer digits followed by a unit (H, M, S, m, u, n).
- Inspect intermediary proxies/LBs/service meshes for header rewriting that truncates grpc-timeout.
- If you control the client, rely on context deadlines (gRPC encodes them automatically) rather than setting the header by hand.
Example fix
// before: hand-set, truncated header
// metadata: {"grpc-timeout": ""}
// after: let gRPC derive it from the context deadline
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := client.Method(ctx, req) // gRPC emits e.g. "5S" Defensive patterns
Strategy: validation
Validate before calling
// If you build grpc-timeout manually, ensure length >= 2 (digits + unit).
func encodeGrpcTimeout(d time.Duration) (string, error) {
if d < 0 {
return "", fmt.Errorf("negative timeout")
}
// prefer gRPC's own encoding; this is illustrative.
return fmt.Sprintf("%dS", int64(d.Seconds())), nil
} Prevention
- Let gRPC encode grpc-timeout from context.WithTimeout rather than setting the header.
- Audit intermediaries that might truncate or clear the header.
When it happens
Trigger: An inbound grpc-timeout metadata value of length 0 or 1 reaches decodeTimeout. Produced by a client/proxy that sets an empty or single-char timeout header, or that truncates it. Since gRPC clients normally emit well-formed timeouts, this usually points to a hand-crafted caller, a buggy intermediary, or header corruption.
Common situations: A proxy/LB that rewrites or truncates grpc-timeout; a non-gRPC client (raw HTTP/2) setting the header incorrectly; a middleware that clears headers under some path; fuzzed or malformed traffic.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- transport: timeout string is too long
- transport: timeout unit is not recognized
- received an illegal stream id
- received -bytes data exceeding the limit bytes
- ErrCodeEnhanceYourCalm
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/92cf394d7314dca4.
Report an issue: GitHub.
Appendix: source
Thrown at internal/transport/http_util.go:191
case minute:
return time.Minute, true
case second:
return time.Second, true
case millisecond:
return time.Millisecond, true
case microsecond:
return time.Microsecond, true
case nanosecond:
return time.Nanosecond, true
default:
}
return
}
func decodeTimeout(s string) (time.Duration, error) {
size := len(s)
if size < 2 {
return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
}
if size > 9 {
// Spec allows for 8 digits plus the unit.
return 0, fmt.Errorf("transport: timeout string is too long: %q", s)
}
unit := timeoutUnit(s[size-1])
d, ok := timeoutUnitToDuration(unit)
if !ok {
return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
}
t, err := strconv.ParseUint(s[:size-1], 10, 64)
if err != nil {
return 0, err
}
const maxHours = math.MaxInt64 / uint64(time.Hour)
if d == time.Hour && t > maxHours {
// This timeout would overflow math.MaxInt64; clamp it.
return time.Duration(math.MaxInt64), nilView on GitHub (pinned to 0c51461d27)