grpc/grpc-go · critical

metadata: Pairs got the odd number of input pairs for…

Error message

metadata: Pairs got the odd number of input pairs for metadata: %d

What it means

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.

Solutions

  1. Always pass key/value pairs: an even number of string arguments.
  2. When building the slice dynamically, assert len(kv)%2 == 0 before calling Pairs.
  3. Use metadata.New(map[string]string{...}) or md.Append(k, v) for less error-prone construction.

Example fix

// before
md := metadata.Pairs("trace-id", traceID, "user-id") // odd -> panic

// after
md := metadata.Pairs("trace-id", traceID, "user-id", userID)
// or
md := metadata.New(map[string]string{"trace-id": traceID, "user-id": userID})
Defensive patterns

Strategy: validation

Validate before calling

// Validate parity before calling Pairs.
func safePairs(kv ...string) metadata.MD {
    if len(kv)%2 == 1 {
        return metadata.MD{} // or return an error from your wrapper
    }
    return metadata.Pairs(kv...)
}

Prevention

When it happens

Trigger: Calling metadata.Pairs("key") (one arg), metadata.Pairs("a","1","b") (three args), or building the kv slice dynamically and accidentally dropping a value.

Common situations: A typo omitting a value; programmatically appending metadata keys and missing the paired value; a refactor that splits a pair across a conditional.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/e0ed8db280ac0ee1. Report an issue: GitHub.

Appendix: 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 for

View on GitHub (pinned to 0c51461d27)