grpc/grpc-go · critical
metadata: Pairs got the odd number of input pairs for metada
Error message
metadata: Pairs got the odd number of input pairs for metadata: %d
What it means
metadata.Pairs (metadata.go:82) builds a metadata map from variadic key/value string arguments. It panics when the argument count is odd, because a key would be left without a value. This is a hard programmer error: the function cannot guess the missing value, so it fails fast rather than producing corrupt metadata.
Source
Thrown at metadata/metadata.go:84
return md
}
// Pairs returns an MD formed by the mapping of key, value ...
// Pairs panics if len(kv) is odd.
//
// Only the following ASCII characters are allowed in keys:
// - digits: 0-9
// - uppercase letters: A-Z (normalized to lower)
// - lowercase letters: a-z
// - special characters: -_.
//
// Uppercase letters are automatically converted to lowercase.
//
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
// result in errors if set in metadata.
func Pairs(kv ...string) MD {
if len(kv)%2 == 1 {
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
}
md := make(MD, len(kv)/2)
for i := 0; i < len(kv); i += 2 {
key := strings.ToLower(kv[i])
md[key] = append(md[key], kv[i+1])
}
return md
}
// loggableMetadataKeys is the set of metadata keys whose values are known not
// to carry credentials or other sensitive data, and are therefore safe to print
// verbatim in String. Values for any key not in this set are redacted. Keys are
// in metadata's canonical lowercase form.
//
// The list is intentionally restricted to standardized gRPC and HTTP/2 protocol
// headers. Everything else, including all application-defined keys, is
// censored.
// Note that this list might change as we add/remove support forView on GitHub (pinned to 03255a9237)
Solutions
- Find the metadata.Pairs call in the stack trace and recount the arguments; ensure they come in key,value,key,value order.
- If building pairs from a dynamic slice, validate even length before passing it: if len(kv)%2 != 0 { return error }.
- Use append in fixed (key,value) twos rather than ad-hoc concatenation.
Example fix
// before
md := metadata.Pairs("request-id", id, "trace-id") // odd: trace-id has no value
// after
md := metadata.Pairs("request-id", id, "trace-id", traceID) Defensive patterns
Strategy: validation
Validate before calling
// Validate even parity before calling Pairs with a dynamic slice
if len(kv)%2 != 0 {
return metadata.MD{}, fmt.Errorf("metadata key/value pairs must be even, got %d", len(kv))
}
return metadata.Pairs(kv...), nil Prevention
- Always pass literal key,value pairs rather than building a slice dynamically.
- Add a unit test asserting any helper that constructs metadata produces an even-length slice.
- Enable go vet / linters and review any Pairs(...) with more than 2 args.
When it happens
Trigger: Calling metadata.Pairs(kv...) with an odd number of string arguments, e.g. metadata.Pairs("key1", "v1", "key2"). The panic fires immediately at the len(kv)%2==1 check before any metadata is constructed.
Common situations: A typo or deleted value leaves a dangling key; building pairs from a dynamically-constructed slice that happened to have odd length; copy-paste error when adding a header.
Related errors
- metadata: AppendToOutgoingContext got an odd number of input
- metadata: FromOutgoingContext got an odd number of input pai
- cannot register Codec with empty string result for Name()
- cannot register CodecV2 with empty string result for Name()
- there is an empty key in the header
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/e0ed8db280ac0ee1.
Report an issue: GitHub.