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 for

View on GitHub (pinned to 03255a9237)

Solutions

  1. Find the metadata.Pairs call in the stack trace and recount the arguments; ensure they come in key,value,key,value order.
  2. If building pairs from a dynamic slice, validate even length before passing it: if len(kv)%2 != 0 { return error }.
  3. 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

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


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