grafana/k6 · error
invalid tls value: '%#v', expected (optional) keys: cert, ke
Error message
invalid tls value: '%#v', expected (optional) keys: cert, key, password, and cacerts
What it means
Thrown by k6's gRPC Client.connect() when the tls connect option is not an object. parseConnectTLSParam (internal/js/modules/k6/grpc/params.go:220-226) requires the value of the 'tls' key to be a map (JS object) whose optional keys are cert, key, password, cacerts; anything else — a string, boolean, number, or array — fails the type assertion v.(map[string]any) and produces this error.
Source
Thrown at internal/js/modules/k6/grpc/params.go:225
var ok bool
result.Authority, ok = v.(string)
if !ok {
return result, fmt.Errorf("invalid authority value: '%#v', it needs to be a string", v)
}
default:
return result, fmt.Errorf("unknown connect param: %q", k)
}
}
return result, nil
}
func parseConnectTLSParam(params *connectParams, v any) error {
var ok bool
params.TLS, ok = v.(map[string]any)
if !ok {
return fmt.Errorf("invalid tls value: '%#v', expected (optional) keys: cert, key, password, and cacerts", v)
}
// optional map keys below
if cert, certok := params.TLS["cert"]; certok {
if _, ok = cert.(string); !ok {
return fmt.Errorf("invalid tls cert value: '%#v', it needs to be a PEM formatted string", v)
}
}
if key, keyok := params.TLS["key"]; keyok {
if _, ok = key.(string); !ok {
return fmt.Errorf("invalid tls key value: '%#v', it needs to be a PEM formatted string", v)
}
}
if pass, passok := params.TLS["password"]; passok {
if _, ok = pass.(string); !ok {
return fmt.Errorf("invalid tls password value: '%#v', it needs to be a string", v)
}
}
if cacerts, cacertsok := params.TLS["cacerts"]; cacertsok {View on GitHub (pinned to 93accf6570)
Solutions
- Make the tls option an object literal: tls: { ... } with only the keys cert, key, password, cacerts.
- If you only want the system CAs, pass tls: {} (empty object) or omit the tls option entirely.
- To disable TLS, use plaintext: true instead of tls: false.
- Check the printed %#v value in the message to see what type was actually passed.
Example fix
// before
client.connect('host:443', { tls: true });
// after
client.connect('host:443', { plaintext: false, tls: {} }); Defensive patterns
Strategy: validation
Validate before calling
function validateTlsParam(params = {}) {
if (!('tls' in params)) return;
if (typeof params.tls !== 'object' || params.tls === null || Array.isArray(params.tls)) {
throw new Error('connect param tls must be an object with optional keys cert, key, password, cacerts');
}
} Type guard
const isTlsObject = (v) => v === undefined || (typeof v === 'object' && v !== null && !Array.isArray(v));
Try / catch
try { client.connect(addr, { tls }); } catch (e) { if (/invalid tls value/.test(e.message)) { /* fall back to default TLS: client.connect(addr, {}) */ } throw e; } Prevention
- Remember tls takes no boolean — plaintext: true is the way to disable TLS.
- Centralize TLS config in one helper so the object shape is checked once.
When it happens
Trigger: Passing tls: true (a common mistake from older k6 versions where tls was a boolean), tls: 'cert.pem', tls: [cert], or tls: null exported to a non-object. Note the message prints the whole offending value with %#v, so you see exactly what was received.
Common situations: Scripts written for k6 <0.42-style APIs where TLS was toggled differently; passing a file path string instead of the file contents object; double-wrapping the tls object ({ tls: { tls: {...} } }).
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to decode PEM key
- invalid grpc.connect() parameters: %w
- invalid plaintext value: '%#v', it needs to be boolean
- invalid maxReceiveSize value: '%#v, it needs to be a positiv
- invalid maxSendSize value: '%#v, it needs to be a positive i
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/f04f5fc5343a45ab.
Report an issue: GitHub.