grafana/k6 · error
invalid metadata param: %w
Error message
invalid metadata param: %w
What it means
params.metadata on client.invoke() or new grpc.Stream() is parsed by newMetadata(): it must be a plain object mapping header names to values. Keys ending in -bin must map to binary values ([]byte from JS: Uint8Array/ArrayBuffer); all other keys must map to strings. A non-object value, or any value of the wrong type, fails and is rethrown wrapped as 'invalid metadata param'.
Source
Thrown at internal/js/modules/k6/grpc/params.go:47
func newCallParams(vu modules.VU, input sobek.Value) (*callParams, error) {
result := &callParams{
Metadata: metadata.New(nil),
TagsAndMeta: vu.State().Tags.GetCurrentValues(),
}
if common.IsNullish(input) {
return result, nil
}
rt := vu.Runtime()
params := input.ToObject(rt)
for _, k := range params.Keys() {
switch k {
case "metadata":
md, err := newMetadata(params.Get(k))
if err != nil {
return result, fmt.Errorf("invalid metadata param: %w", err)
}
result.Metadata = md
case "tags":
if err := common.ApplyCustomUserTags(rt, &result.TagsAndMeta, params.Get(k)); err != nil {
return result, fmt.Errorf("metric tags: %w", err)
}
case "timeout":
var err error
v := params.Get(k).Export()
result.Timeout, err = types.GetDurationValue(v)
if err != nil {
return result, fmt.Errorf("invalid timeout value: %w", err)
}
case "discardResponseMessage":
result.DiscardResponseMessage = params.Get(k).ToBoolean()
default:
return result, fmt.Errorf("unknown param: %q", k)View on GitHub (pinned to 93accf6570)
Solutions
- Pass a plain object: { authorization: 'token' }
- Give -bin keys Uint8Array values (e.g. new Uint8Array([2, 200]))
- Stringify non-string scalars: String(count)
Example fix
// before
client.invoke(method, req, { metadata: { 'x-load-tester-bin': 'buffer' } });
// after
client.invoke(method, req, { metadata: { 'x-load-tester-bin': new Uint8Array([2, 200]) } }); Defensive patterns
Strategy: type-guard
Validate before calling
assertGrpcMetadata(params.metadata); client.invoke(method, req, params);
Type guard
function assertGrpcMetadata(obj, where = 'metadata') {
if (obj == null) return true;
if (typeof obj !== 'object' || Array.isArray(obj)) {
throw new Error(`${where} must be a plain object of key-value pairs`);
}
for (const [k, v] of Object.entries(obj)) {
if (k.endsWith('-bin')) {
if (!(v instanceof Uint8Array) && !(v instanceof ArrayBuffer)) {
throw new Error(`${where}["${k}"] must be binary (Uint8Array/ArrayBuffer)`);
}
} else if (typeof v !== 'string') {
throw new Error(`${where}["${k}"] must be a string`);
}
}
return true;
} Prevention
- Always construct metadata as a plain object literal, never a string or array
- Stringify numeric/boolean values before putting them into metadata
- Reserve -bin suffixes for keys whose values come from typed arrays
When it happens
Trigger: metadata: 'authorization=token' (a string instead of an object); metadata: ['a', 'b'] (an array); { 'trace-id-bin': 'xyz' } (string for a -bin key); { count: 5 } (number for a normal key).
Common situations: Porting HTTP header strings or curl-style 'k: v' syntax; passing base64 strings where the server expects binary metadata; numbers and booleans not stringified before being spread into metadata.
Related errors
- %q value must be binary
- %q value must be a string
- invalid reflectMetadata param: %w
- Unexpected end of selector while parsing selector `${selecto
- must be an object with key-value pairs
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/8886526db6f110d9.
Report an issue: GitHub.