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

  1. 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).
  2. Inspect intermediary proxies/LBs/service meshes for header rewriting that truncates grpc-timeout.
  3. 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

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

Related errors


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), nil

View on GitHub (pinned to 0c51461d27)