{"record":{"id":"ca1ffcbe273f646f","repo":"grafana/k6","slug":"must-be-an-object-with-key-value-pairs","errorCode":null,"errorMessage":"must be an object with key-value pairs","messagePattern":"must be an object with key-value pairs","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/grpc/params.go","lineNumber":84,"sourceCode":"\t\t}\n\t}\n\n\treturn result, nil\n}\n\n// newMetadata constructs a metadata.MD from the input value.\nfunc newMetadata(input sobek.Value) (metadata.MD, error) {\n\tmd := metadata.New(nil)\n\n\tif common.IsNullish(input) {\n\t\treturn md, nil\n\t}\n\n\tv := input.Export()\n\n\trawHeaders, ok := v.(map[string]any)\n\tif !ok {\n\t\treturn md, errors.New(\"must be an object with key-value pairs\")\n\t}\n\n\tfor hk, kv := range rawHeaders {\n\t\tvar val string\n\t\t// The gRPC spec defines that Binary-valued keys end in -bin\n\t\t// https://grpc.io/docs/what-is-grpc/core-concepts/#metadata\n\t\tif strings.HasSuffix(hk, \"-bin\") {\n\t\t\tvar binVal []byte\n\t\t\tif binVal, ok = kv.([]byte); !ok {\n\t\t\t\treturn md, fmt.Errorf(\"%q value must be binary\", hk)\n\t\t\t}\n\n\t\t\t// https://github.com/grpc/grpc-go/blob/v1.57.0/Documentation/grpc-metadata.md#storing-binary-data-in-metadata\n\t\t\tval = string(binVal)\n\t\t} else if val, ok = kv.(string); !ok {\n\t\t\treturn md, fmt.Errorf(\"%q value must be a string\", hk)\n\t\t}\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/grpc/params.go#L66-L102","documentation":"newMetadata (internal/js/modules/k6/grpc/params.go:73) converts gRPC metadata from JavaScript into Go's metadata.MD. It exports the value and requires a `map[string]any`; anything else — arrays, strings, numbers, class instances — fails the assertion with \"must be an object with key-value pairs\". It is reached from the `metadata` field of call/stream params and the `reflectMetadata` field of connect params.","triggerScenarios":"Passing `metadata: 'authorization: token'` (string), `metadata: [['k','v']]` (array), `metadata: 42`, or a non-plain object (e.g. a Map or class instance) in the params of connect/invoke/stream: `client.connect(addr, { reflectMetadata: [] })` or `new grpc.Stream(client, method, { metadata: 'k' })`.","commonSituations":"Porting curl-style header strings or header arrays from HTTP code into gRPC params; using a JS Map because the docs show object syntax; building metadata with a helper that returns a non-plain object.","solutions":["Pass a plain object literal: `metadata: { 'authorization': 'token k6' }`","Convert arrays/Maps to a plain object before the call","Use string values for normal keys and []byte (keys ending in -bin) for binary keys"],"exampleFix":"// before\nclient.connect(addr, { reflectMetadata: [['api-key', 'secret']] });\n\n// after\nclient.connect(addr, { reflectMetadata: { 'api-key': 'secret' } });","handlingStrategy":"validation","validationCode":"const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);\nif (!isPlainObject(metadata)) { throw new TypeError('metadata must be a plain object of key/value pairs'); }","typeGuard":"const isMetadataObject = (v) =>\n  typeof v === 'object' && v !== null && !Array.isArray(v) &&\n  Object.values(v).every((x) => typeof x === 'string');","tryCatchPattern":"try {\n  client.connect(addr, { reflectMetadata: md });\n} catch (e) {\n  if (String(e.message).includes('must be an object with key-value pairs')) throw new Error('reflectMetadata/metadata must be { key: value }, got: ' + JSON.stringify(md));\n  throw e;\n}","preventionTips":["Use object literals for metadata: { 'authorization': 'token ...' }","Convert Map/array header formats at the boundary: Object.fromEntries(map)","Keep metadata values as strings; only *-bin keys take binary values"],"tags":["grpc","metadata","validation","javascript"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}