grafana/k6 · error
invalid tls cacerts value: '%#v', it needs to be a string or
Error message
invalid tls cacerts value: '%#v', it needs to be a string or an array of PEM formatted strings
What it means
Thrown by k6's gRPC Client.connect() when tls.cacerts is an array but one of its entries is not a string. parseConnectTLSParam (internal/js/modules/k6/grpc/params.go:243-251) accepts cacerts either as a single PEM string or as an array; when it is an array, every element must itself be a PEM string. The message prints the whole tls map (v), not the offending entry.
Source
Thrown at internal/js/modules/k6/grpc/params.go:248
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',"+
" it needs to be a string or an array of PEM formatted strings", v)
}
}
} else if _, ok = cacerts.(string); !ok {
return fmt.Errorf("invalid tls cacerts value: '%#v',"+
" it needs to be a string or an array of PEM formatted strings", v)
}
}
return nil
}
View on GitHub (pinned to 93accf6570)
Solutions
- Ensure every element of the cacerts array is a PEM string: cacerts.filter((c) => typeof c === 'string').
- Flatten nested arrays before passing: cacerts.flat().
- For a single CA, the plain string form cacerts: pem is also accepted.
Example fix
// before
const cas = ['-----BEGIN...', undefined];
client.connect('host:443', { tls: { cacerts: cas } });
// after
const cas = ['-----BEGIN...', '-----BEGIN...'].filter((c) => typeof c === 'string');
client.connect('host:443', { tls: { cacerts: cas } }); Defensive patterns
Strategy: validation
Validate before calling
function normalizeCacerts(cacerts) {
const arr = Array.isArray(cacerts) ? cacerts.flat() : [cacerts];
const pems = arr.filter((c) => typeof c === 'string' && c.includes('-----BEGIN'));
if (pems.length !== arr.length) throw new Error('cacerts entries must all be PEM strings');
return Array.isArray(cacerts) ? pems : pems[0];
} Type guard
const isCacerts = (v) => typeof v === 'string' || (Array.isArray(v) && v.every((c) => typeof c === 'string'));
Prevention
- Build cacerts arrays with .filter((c) => typeof c === 'string') to drop failed reads.
- Flatten nested arrays before passing them.
When it happens
Trigger: tls: { cacerts: ['-----BEGIN...', 42] }, cacerts: [['pem1', 'pem2']] (nested array), or an array containing null/objects. Mixing a string and a non-string in the array still fails because the loop checks each entry.
Common situations: Building the cacerts array dynamically (e.g. .filter() forgotten, a failed file read returning undefined pushed into the array); concatenating arrays where one element is a parsed certificate object.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- invalid tls value: '%#v', expected (optional) keys: cert, ke
- invalid tls cert value: '%#v', it needs to be a PEM formatte
- invalid tls key value: '%#v', it needs to be a PEM formatted
- invalid tls password value: '%#v', it needs to be a string
- failed to decode PEM key
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/9171706a4a47fe6d.
Report an issue: GitHub.