grafana/k6 · error
failed to decode second prime factor: %w
Error message
failed to decode second prime factor: %w
What it means
For RSA private-key JWK import, the second prime factor q is decoded with base64.RawURLEncoding after p. This error means q failed that decode — the same failure modes as the other fields: '=' padding, '+'/'/' alphabet, embedded whitespace, or impossible length. It is returned from crypto.subtle.importKey('jwk', ...) for RSA algorithms.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:366
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)
if err != nil {
return nil, UnknownCryptoKeyType, 0, fmt.Errorf("failed to decode coefficient: %w", err)
}
privKey := &rsa.PrivateKey{
PublicKey: pubKey,
D: new(big.Int).SetBytes(dBytes),
Primes: []*big.Int{View on GitHub (pinned to 93accf6570)
Solutions
- Convert q to unpadded base64url
- Remove whitespace and padding
- Check the decoded length is about half the modulus
- Ensure p and q come from the same key and are in the correct JWK slots
Example fix
// before
const jwk = { kty: 'RSA', n, e: 'AQAB', d, p, q: paddedQ, dp, dq, qi };
// after
const toB64u = (s) => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').replace(/\s+/g, '');
const jwk = { kty: 'RSA', n, e: 'AQAB', d, p, q: toB64u(paddedQ), dp, dq, qi }; Defensive patterns
Strategy: validation
Validate before calling
const B64URL = /^[A-Za-z0-9_-]+$/;
if (jwk.q !== undefined) {
const q = String(jwk.q).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '').replace(/\s+/g, '');
if (!B64URL.test(q)) throw new Error('prime q is not unpadded base64url');
jwk = { ...jwk, q };
} Type guard
function isB64uPrime(s) {
return typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s) && s.length >= 20;
} Try / catch
try {
key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);
} catch (e) {
if (e.message.includes('failed to decode second prime factor')) {
jwk = { ...jwk, q: toB64u(jwk.q) };
key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);
} else throw e;
} Prevention
- Apply the same unpadded base64url normalization to q as to p
- Ensure p and q come from the same key and are not swapped
- Watch for truncation at the tail of long JWK strings
When it happens
Trigger: q encoded with padding or the standard base64 alphabet; q truncated; q containing line breaks; q swapped with p is not this error's cause (both decode fine and the swap fails later at Validate()).
Common situations: Same as p: PEM-derived or vault-stored values re-encoded incorrectly; copy-paste truncation of the last fields of long JWKs.
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 private exponent: %w
- failed to decode first 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/70780ae9917d038e.
Report an issue: GitHub.