grafana/k6 · error
invalid WebSocket tags option: %w
Error message
invalid WebSocket tags option: %w
What it means
new WebSocket(url, { tags: {...} }) validates each tag through common.ApplyCustomUserTags before connecting. If a tag value is not a string, boolean, or number (e.g. an object, array, or nested structure), the apply fails and k6 wraps it as 'invalid WebSocket tags option'. Only flat, primitive-valued tags are representable in k6's metric engine.
Source
Thrown at internal/js/modules/k6/websockets/params.go:59
params := raw.ToObject(rt)
for _, k := range params.Keys() {
switch k {
case "headers":
headersV := params.Get(k)
if common.IsNullish(headersV) {
continue
}
headersObj := headersV.ToObject(rt)
if headersObj == nil {
continue
}
for _, key := range headersObj.Keys() {
parsed.headers.Set(key, headersObj.Get(key).String())
}
case "tags":
if err := common.ApplyCustomUserTags(rt, parsed.tagsAndMeta, params.Get(k)); err != nil {
return nil, fmt.Errorf("invalid WebSocket tags option: %w", err)
}
case "jar":
jarV := params.Get(k)
if common.IsNullish(jarV) {
continue
}
if v, ok := jarV.Export().(*httpModule.CookieJar); ok {
parsed.cookieJar = v.Jar
}
case "compression":
// deflate compression algorithm is supported - as defined in RFC7692
// compression here relies on the implementation in gorilla/websocket package, usage is
// experimental and may result in decreased performance. package supports
// only "no context takeover" scenario
algoString := strings.TrimSpace(params.Get(k).ToString().String())
if algoString != "deflate" {
return nil, fmt.Errorf("unsupported compression algorithm '%s', supported algorithm is 'deflate'", algoString)View on GitHub (pinned to 93accf6570)
Solutions
- Flatten every tag value to a primitive: tenant: String(obj.id) or JSON.stringify for complex values
- Whitelist only the scalar keys you need when spreading: { tags: { name: cfg.name, env: cfg.env } } where both are strings/numbers
- Sanitize with a helper that drops or stringifies non-primitives before constructing the WebSocket
- Check the wrapped cause text — it names the offending value/type
Example fix
// before
const ws = new WebSocket(url, { tags: { user: userObj } }); // userObj is {id:1} -> invalid
// after
const ws = new WebSocket(url, { tags: { user: String(userObj.id) } }); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeTags(tags) {
const out = {};
for (const [k, v] of Object.entries(tags || {})) {
if (v == null) continue;
const t = typeof v;
if (t === 'string' || t === 'number' || t === 'boolean') out[k] = v;
else out[k] = JSON.stringify(v);
}
return out;
}
const sock = new WebSocket(url, { tags: sanitizeTags(cfgTags) }); Type guard
const isPrimitiveTag = v => v == null ? false : ['string','number','boolean'].includes(typeof v);
Try / catch
try { new WebSocket(url, { tags }); } catch (e) { if (/tags option/.test(e.message)) throw new Error(`tag values must be string/number/boolean: ${e.message}`); throw e; } Prevention
- Keep WebSocket tags flat and scalar; stringify anything structured
- Beware low cardinality: stringified JSON tags multiply metric series
- Lint your options object once at script start with the sanitize helper
- Remember tag rules match the HTTP APIs — reuse the same sanitize code
When it happens
Trigger: new WebSocket(url, { tags: { tenant: { id: 7 } } }), tags: { list: ['a','b'] }, or any tags object whose export is a map containing non-primitive values. The headers/compression cases in the same parse loop succeed; only the tags branch produces this message.
Common situations: Reusing an options object built for HTTP requests where a tag value happens to be an object; spreading a config object into tags and accidentally including nested keys; migrating scripts where tags were previously silently coerced; putting the URL or params object itself into tags.
Related errors
- metric tags: %w
- failed to process [blobParts]: %w
- unsupported type: %T
- invalid value for metric tag '%s': only String, Boolean and
- Unexpected end of selector while parsing selector `${selecto
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/2c0826fe3c5186f8.
Report an issue: GitHub.