grafana/k6 · error
invalid tls password value: '%#v', it needs to be a string
Error message
invalid tls password value: '%#v', it needs to be a string
What it means
Thrown by k6's gRPC Client.connect() when tls.password is present but not a string. parseConnectTLSParam (internal/js/modules/k6/grpc/params.go:238-241) validates only the type: the passphrase used to decrypt an encrypted client private key must be a string. The message again prints the whole tls map (v), not just the password value.
Source
Thrown at internal/js/modules/k6/grpc/params.go:240
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',"+
" 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
- Coerce the passphrase to a string before passing: password: String(__ENV.KEY_PASS).
- If the key is not encrypted, remove the password entry entirely.
- Verify with typeof password === 'string' in script setup code before connect().
Example fix
// before
client.connect('host:443', { tls: { cert, key, password: 12345 } });
// after
client.connect('host:443', { tls: { cert, key, password: String(__ENV.KEY_PASS) } }); Defensive patterns
Strategy: validation
Validate before calling
function validateTls(tls = {}) {
if ('password' in tls && typeof tls.password !== 'string') throw new Error('tls.password must be a string');
} Type guard
const isOptionalString = (v) => v === undefined || typeof v === 'string';
Prevention
- Wrap env-sourced passphrases with String(...) at init time.
- Drop the password key entirely for unencrypted keys.
When it happens
Trigger: tls: { cert, key, password: 12345 }, password: true, or password: ['secret']. Any non-string value under tls.password triggers this before a connection is attempted.
Common situations: Reading the passphrase from an environment variable or config system that yields a non-string (number, object); leaving a placeholder like password: null after debugging.
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 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/3645bd214c88f87d.
Report an issue: GitHub.