{"record":{"id":"e1d74469e1edc335","repo":"grpc/grpc-go","slug":"metadata-appendtooutgoingcontext-got-an-odd-numbe","errorCode":null,"errorMessage":"metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d","messagePattern":"metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"metadata/metadata.go","lineNumber":252,"sourceCode":"// not be modified after calling this function.\nfunc NewIncomingContext(ctx context.Context, md MD) context.Context {\n\treturn context.WithValue(ctx, mdIncomingKey{}, md)\n}\n\n// NewOutgoingContext creates a new context with outgoing md attached. If used\n// in conjunction with AppendToOutgoingContext, NewOutgoingContext will\n// overwrite any previously-appended metadata. md must not be modified after\n// calling this function.\nfunc NewOutgoingContext(ctx context.Context, md MD) context.Context {\n\treturn context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})\n}\n\n// AppendToOutgoingContext returns a new context with the provided kv merged\n// with any existing metadata in the context. Please refer to the documentation\n// of Pairs for a description of kv.\nfunc AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {\n\tif len(kv)%2 == 1 {\n\t\tpanic(fmt.Sprintf(\"metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d\", len(kv)))\n\t}\n\tmd, _ := ctx.Value(mdOutgoingKey{}).(rawMD)\n\tadded := make([][]string, len(md.added)+1)\n\tcopy(added, md.added)\n\tkvCopy := make([]string, 0, len(kv))\n\tfor i := 0; i < len(kv); i += 2 {\n\t\tkvCopy = append(kvCopy, strings.ToLower(kv[i]), kv[i+1])\n\t}\n\tadded[len(added)-1] = kvCopy\n\treturn context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})\n}\n\n// FromIncomingContext returns the incoming metadata in ctx if it exists.\n//\n// All keys in the returned MD are lowercase.\nfunc FromIncomingContext(ctx context.Context) (MD, bool) {\n\tmd, ok := ctx.Value(mdIncomingKey{}).(MD)\n\tif !ok {","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/metadata/metadata.go#L234-L270","documentation":"metadata.AppendToOutgoingContext (metadata/metadata.go:250) appends key/value pairs to the outgoing metadata stored in a context and panics at line 251-253 when len(kv) is odd, for the same reason as Pairs: every key needs a value. The pairs are stored (lowercased keys) and later merged on RPC send.","triggerScenarios":"Calling metadata.AppendToOutgoingContext(ctx, \"key\") with an odd argument count; dynamically building the kv varargs and dropping a value; a helper that conditionally appends a key without its value.","commonSituations":"Building per-RPC metadata with a typo; a conditional that appends a header name but skips its value in some branch; refactoring call sites and losing an argument.","solutions":["Pass an even number of string arguments (key, value, key, value, ...).","Validate len(kv)%2 == 0 before calling, especially when kv is built dynamically.","Prefer metadata.NewOutgoingContext(ctx, md) with a constructed MD for complex metadata."],"exampleFix":"// before\nctx = metadata.AppendToOutgoingContext(ctx, \"auth\", token, \"request-id\") // odd -> panic\n\n// after\nctx = metadata.AppendToOutgoingContext(ctx, \"auth\", token, \"request-id\", reqID)","handlingStrategy":"validation","validationCode":"func safeAppend(ctx context.Context, kv ...string) context.Context {\n    if len(kv)%2 == 1 {\n        return ctx // skip malformed append\n    }\n    return metadata.AppendToOutgoingContext(ctx, kv...)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass key/value pairs as an even-length vararg list.","Validate len(kv)%2 == 0 when kv is assembled dynamically.","For complex metadata, build an MD and use metadata.NewOutgoingContext."],"tags":["metadata","context","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"}