grafana/k6 · error
failed to decode private exponent: %w
Error message
failed to decode private exponent: %w
What it means
When an RSA JWK contains a non-empty d field it is treated as a private key, and d (the private exponent) is decoded with base64.RawURLEncoding. This error means d failed that decoding — padding characters, wrong alphabet, whitespace, or an impossible length. It is raised from crypto.subtle.importKey('jwk', ...) for RSA algorithms.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:358
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode exponent: %w", err)
}
// convert exponent to an integer
eInt := new(big.Int).SetBytes(eBytes).Int64()
pubKey := rsa.PublicKey{
N: new(big.Int).SetBytes(nBytes),
E: int(eInt),
}
// if the private exponent is missing, return the public key
if jwk.D == "" {
return pubKey, PublicCryptoKeyType, pubKey.N.BitLen(), nil
}
dBytes, err := base64URLDecode(jwk.D)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode private exponent: %w", err)
}
pBytes, err := base64URLDecode(jwk.P)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode first prime factor: %w", err)
}
qBytes, err := base64URLDecode(jwk.Q)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode second prime factor: %w", err)
}
dpBytes, err := base64URLDecode(jwk.Dp)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode first exponent: %w", err)
}
dqBytes, err := base64URLDecode(jwk.Dq)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode second exponent: %w", err)
}
qiBytes, err := base64URLDecode(jwk.Qi)View on GitHub (pinned to 93accf6570)
Solutions
- Convert d to unpadded base64url before import
- Remove whitespace and padding from the value
- Check the decoded length: for RSA-2048, d is typically 256 bytes
- If d is unavailable/corrupt but only public operations are needed, drop d (and the other private fields) to import a public key
Example fix
// before
const jwk = { kty: 'RSA', n, e: 'AQAB', d: paddedD, p, q, dp, dq, qi };
// after
const toB64u = (s) => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').replace(/\s+/g, '');
const jwk = { kty: 'RSA', n, e: 'AQAB', d: toB64u(paddedD), p, q, dp, dq, qi }; Defensive patterns
Strategy: validation
Validate before calling
const B64URL = /^[A-Za-z0-9_-]+$/;
if (jwk.d !== undefined && jwk.d !== '') {
const d = String(jwk.d).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').replace(/\s+/g, '');
if (!B64URL.test(d)) throw new Error('private exponent d is not unpadded base64url');
jwk = { ...jwk, d };
} Type guard
function isB64uPrivateExponent(s) {
return typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s) && s.length >= 40;
} Try / catch
try {
key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);
} catch (e) {
if (e.message.includes('failed to decode private exponent')) {
jwk = { ...jwk, d: toB64u(jwk.d) };
key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);
} else throw e;
} Prevention
- Normalize d to unpadded base64url when loading secrets
- If d is unusable and only public operations are needed, strip all private fields
- Verify d survived secret-store transport intact (length check)
When it happens
Trigger: d in standard/padded base64; d containing '+' or '/'; d truncated by secret-store size limits or templating; d with embedded newlines from PEM-style wrapping.
Common situations: Private JWKs sourced from vaults that re-encode or wrap values; manual assembly of private JWKs from PEM dumps; secrets passed through environment variables with mangled characters.
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
- failed to decode first prime factor: %w
- failed to decode second prime factor: %w
- failed to decode first exponent: %w
- failed to decode D: %w
- failed to decode modulus: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/c53ed86c7b548b91.
Report an issue: GitHub.