grafana/k6 · error
invalid tls cert value: '%#v', it needs to be a PEM formatte
Error message
invalid tls cert value: '%#v', it needs to be a PEM formatted string
What it means
Thrown by k6's gRPC Client.connect() when the tls.cert sub-option is present but not a string. parseConnectTLSParam (internal/js/modules/k6/grpc/params.go:228-231) only checks that cert is a Go string (a PEM-formatted client certificate); it does not validate that the PEM parses. Note a formatting quirk: the message interpolates v — the entire tls object — not just the cert value, so the printed value looks like a map.
Source
Thrown at internal/js/modules/k6/grpc/params.go:230
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 {
var cacertsArray []any
if cacertsArray, ok = cacerts.([]any); ok {
for _, cacertsArrayEntry := range cacertsArray {
if _, ok = cacertsArrayEntry.(string); !ok {
return fmt.Errorf("invalid tls cacerts value: '%#v',"+View on GitHub (pinned to 93accf6570)
Solutions
- Ensure tls.cert is a single PEM string, e.g. cert: open('client.pem').readAll()?.toString() in newer k6, or an embedded template literal.
- Keep the PEM headers (-----BEGIN CERTIFICATE-----) intact; do not base64-encode or JSON-wrap the value.
- If you need multiple CA certificates, put them in cacerts (string or array), not cert.
- Ignore the map-looking %#v in the message — it is the whole tls value; check only your cert entry's type.
Example fix
// before
client.connect('host:443', { tls: { cert: [certPem] } });
// after
client.connect('host:443', { tls: { cert: certPem, key: keyPem } }); Defensive patterns
Strategy: validation
Validate before calling
function validateTls(tls = {}) {
if ('cert' in tls && typeof tls.cert !== 'string') throw new Error('tls.cert must be a PEM string');
} Type guard
const isPemString = (v) => typeof v === 'string' && /-----BEGIN [^-]+-----/.test(v);
Prevention
- Only cacerts accepts arrays; cert and key are single strings.
- Read PEM files once at init and assert they begin with '-----BEGIN'.
When it happens
Trigger: tls: { cert: 123 }, cert: { pem: '...' }, cert: ['...'], or passing an array of certificates. cert must be a single string; there is no array form for the client certificate.
Common situations: Confusing cacerts (which accepts a string OR array) with cert (string only); passing a parsed object or Buffer-like value instead of the raw PEM text read from a file.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- invalid tls key value: '%#v', it needs to be a PEM formatted
- invalid tls value: '%#v', expected (optional) keys: cert, ke
- invalid tls password value: '%#v', it needs to be a string
- invalid tls cacerts value: '%#v', it needs to be a string or
- failed to decode PEM key
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/9cd0dd5c5b42869f.
Report an issue: GitHub.