grafana/k6 · error
failed to decode symmetric key: %w
Error message
failed to decode symmetric key: %w
What it means
Thrown by k6 WebCrypto when the k field of a symmetric JWK is not valid base64url encoding. After validation succeeds, extractSymmetricJWK (internal/js/modules/k6/webcrypto/jwk.go:60-63) runs base64URLDecode on k; standard base64 characters ('+', '/'), bad padding, or non-alphabet characters fail and the underlying decoding error is wrapped with %w.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:62
}
return nil
}
// extractSymmetricJWK extracts the symmetric key from a given JWK key (JSON data).
func extractSymmetricJWK(jsonKeyData []byte) ([]byte, error) {
sk := symmetricJWK{}
if err := json.Unmarshal(jsonKeyData, &sk); err != nil {
return nil, fmt.Errorf("failed to parse symmetric JWK: %w", err)
}
if err := sk.validate(); err != nil {
return nil, fmt.Errorf("invalid symmetric JWK: %w", err)
}
skBytes, err := base64URLDecode(sk.K)
if err != nil {
return nil, fmt.Errorf("failed to decode symmetric key: %w", err)
}
return skBytes, nil
}
// exportSymmetricJWK exports a symmetric key as a map of JWK key parameters.
func exportSymmetricJWK(key *CryptoKey) (*JsonWebKey, error) {
rawKey, ok := key.handle.([]byte)
if !ok {
return nil, errors.New("key's handle isn't a byte slice")
}
// wrap result into the object that is expected to be returned
exported := &JsonWebKey{}
exported.Set("k", base64URLEncode(rawKey))
exported.Set("kty", JWKOctKeyType)
exported.Set("ext", key.Extractable)View on GitHub (pinned to 93accf6570)
Solutions
- Convert standard base64 to base64url: replace '+' with '-', '/' with '_', and strip '=' padding (or use k6's encoding/base64: Base64.encodings.Base64Url without padding).
- Ensure no whitespace or newlines inside k.
- If the secret is hex, decode hex to bytes and re-encode as base64url before putting it in the JWK.
Example fix
// before
const k = 'a+b/cd=='; // standard base64 with padding
await crypto.subtle.importKey('jwk', { kty: 'oct', k }, alg, false, ['encrypt']);
// after
import encoding from 'k6/encoding';
const k = encoding.b64encode(encoding.b64decode('a+b/cd==', 'std'), 'url').replace(/=+$/, '');
await crypto.subtle.importKey('jwk', { kty: 'oct', k }, alg, false, ['encrypt']); Defensive patterns
Strategy: validation
Validate before calling
function toBase64url(b64) {
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
const k = toBase64url(rawSecretB64); // then { kty: 'oct', k } Type guard
const isBase64Url = (s) => typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s);
Prevention
- Store secrets pre-encoded as unpadded base64url in environments.
- Strip whitespace/newlines from pasted key material before use.
When it happens
Trigger: k contains standard-base64 ('+'/'/' characters) instead of base64url ('-'/'_'); k has trailing '=' padding that violates the strict decoder; k includes whitespace/newlines from being wrapped in a certificate-style layout; k is hex-encoded rather than base64.
Common situations: Secrets copied from JWT headers or vaults that emit standard base64; line-wrapped base64 pasted from terminals; secrets stored as hex by another system.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- invalid key type: %s
- failed to parse symmetric JWK: %w
- invalid symmetric JWK: %w
- failed to decode X coordinate: %w
- failed to decode Y coordinate: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/eb9ab90fca94d5d2.
Report an issue: GitHub.