{"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/03255a9237b6eb32710f6bc4f2de9a675b99fe36/metadata/metadata.go#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmd := metadata.Pairs(\"request-id\", id, \"trace-id\") // odd: trace-id has no value\n\n// after\nmd := metadata.Pairs(\"request-id\", id, \"trace-id\", traceID)","handlingStrategy":"validation","validationCode":"// Validate even parity before calling Pairs with a dynamic slice\nif len(kv)%2 != 0 {\n    return metadata.MD{}, fmt.Errorf(\"metadata key/value pairs must be even, got %d\", len(kv))\n}\nreturn metadata.Pairs(kv...), nil","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["metadata","panic","grpc-go","validation"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}