{"record":{"id":"e0ed8db280ac0ee1","repo":"grpc/grpc-go","slug":"metadata-pairs-got-the-odd-number-of-input-pairs","errorCode":null,"errorMessage":"metadata: Pairs got the odd number of input pairs for metadata: %d","messagePattern":"metadata: Pairs got the odd number of input pairs for metadata: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"metadata/metadata.go","lineNumber":84,"sourceCode":"\treturn md\n}\n\n// Pairs returns an MD formed by the mapping of key, value ...\n// Pairs panics if len(kv) is odd.\n//\n// Only the following ASCII characters are allowed in keys:\n//   - digits: 0-9\n//   - uppercase letters: A-Z (normalized to lower)\n//   - lowercase letters: a-z\n//   - special characters: -_.\n//\n// Uppercase letters are automatically converted to lowercase.\n//\n// Keys beginning with \"grpc-\" are reserved for grpc-internal use only and may\n// result in errors if set in metadata.\nfunc Pairs(kv ...string) MD {\n\tif len(kv)%2 == 1 {\n\t\tpanic(fmt.Sprintf(\"metadata: Pairs got the odd number of input pairs for metadata: %d\", len(kv)))\n\t}\n\tmd := make(MD, len(kv)/2)\n\tfor i := 0; i < len(kv); i += 2 {\n\t\tkey := strings.ToLower(kv[i])\n\t\tmd[key] = append(md[key], kv[i+1])\n\t}\n\treturn md\n}\n\n// loggableMetadataKeys is the set of metadata keys whose values are known not\n// to carry credentials or other sensitive data, and are therefore safe to print\n// verbatim in String. Values for any key not in this set are redacted. Keys are\n// in metadata's canonical lowercase form.\n//\n// The list is intentionally restricted to standardized gRPC and HTTP/2 protocol\n// headers. Everything else, including all application-defined keys, is\n// censored.\n// Note that this list might change as we add/remove support for","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/metadata/metadata.go#L66-L102","documentation":"metadata.Pairs (metadata/metadata.go:82) builds an MD from alternating key/value string arguments and panics at line 83-85 when len(kv) is odd, because a dangling key with no value is meaningless. Keys are lowercased and stored; values are appended. The doc (line 70) states Pairs panics on odd input.","triggerScenarios":"Calling metadata.Pairs(\"key\") (one arg), metadata.Pairs(\"a\",\"1\",\"b\") (three args), or building the kv slice dynamically and accidentally dropping a value.","commonSituations":"A typo omitting a value; programmatically appending metadata keys and missing the paired value; a refactor that splits a pair across a conditional.","solutions":["Always pass key/value pairs: an even number of string arguments.","When building the slice dynamically, assert len(kv)%2 == 0 before calling Pairs.","Use metadata.New(map[string]string{...}) or md.Append(k, v) for less error-prone construction."],"exampleFix":"// before\nmd := metadata.Pairs(\"trace-id\", traceID, \"user-id\") // odd -> panic\n\n// after\nmd := metadata.Pairs(\"trace-id\", traceID, \"user-id\", userID)\n// or\nmd := metadata.New(map[string]string{\"trace-id\": traceID, \"user-id\": userID})","handlingStrategy":"validation","validationCode":"// Validate parity before calling Pairs.\nfunc safePairs(kv ...string) metadata.MD {\n    if len(kv)%2 == 1 {\n        return metadata.MD{} // or return an error from your wrapper\n    }\n    return metadata.Pairs(kv...)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass an even number of arguments (key, value, key, value, ...).","When building kv dynamically, assert len(kv)%2 == 0 first.","Prefer metadata.New(map[string]string{...}) for less error-prone construction."],"tags":["metadata","panic","validation","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}