grafana/k6 · error
failed to decode coefficient: %w
Error message
failed to decode coefficient: %w
What it means
Thrown by k6's webcrypto RSA JWK importer when the 'qi' (q inverse, CRT coefficient) member of the imported JWK fails base64url decoding. Like the other CRT fields (dp, dq), qi is mandatory in k6's RSA private-key JWK path and must be unpadded RFC 7515 base64url; the wrapped error carries the exact decoding failure.
Source
Thrown at internal/js/modules/k6/webcrypto/jwk.go:378
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{
new(big.Int).SetBytes(pBytes),
new(big.Int).SetBytes(qBytes),
},
Precomputed: rsa.PrecomputedValues{
Dp: new(big.Int).SetBytes(dpBytes),
Dq: new(big.Int).SetBytes(dqBytes),
Qinv: new(big.Int).SetBytes(qiBytes),
},
}
err = privKey.Validate()
if err != nil {View on GitHub (pinned to 93accf6570)
Solutions
- Export a complete JWK from a working key via crypto.subtle.exportKey('jwk', key) and import that verbatim
- Re-encode qi as unpadded base64url (translate '+'->'-', '/'->'_', drop '=' padding)
- Pre-validate all JWK members with a base64url regex before calling importKey
- Prefer PEM ('spki'/'pkcs8') import when you control the key serialization format
Example fix
// before
const priv = await crypto.subtle.importKey('jwk', exported, alg, true, ['sign']);
// exported.qi is hex: '00aa11bb...'
// after
const b64url = hexToB64Url(exported.qiHex); // encode bytes, then base64url without padding
const priv = await crypto.subtle.importKey('jwk', { ...exported, qi: b64url }, alg, true, ['sign']); Defensive patterns
Strategy: validation
Validate before calling
const b64url = s => s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const NEEDS_FIX = /[+/=]/;
for (const f of ['qi','dp','dq','p','q','d','n','e']) {
if (typeof jwk[f] === 'string' && NEEDS_FIX.test(jwk[f])) jwk[f] = b64url(jwk[f]);
}
await crypto.subtle.importKey('jwk', jwk, alg, true, usages); Type guard
const isB64Url = s => typeof s === 'string' && /^[A-Za-z0-9_-]*$/.test(s); const hasValidQi = j => isB64Url(j.qi);
Try / catch
try { await crypto.subtle.importKey('jwk', jwk, alg, true, usages); }
catch (e) { if (/coefficient/.test(e.message)) console.error('fix jwk.qi encoding'); throw e; } Prevention
- Treat base64url normalization (drop '=', map '+'/'/') as a mandatory step for externally sourced JWKs
- Validate every JWK member with a regex before import — cheap and pinpoints the field
- Keep a canonical copy of keys produced by exportKey for tests
When it happens
Trigger: crypto.subtle.importKey('jwk', jwk, rsaAlg, ...) with jwk.qi containing '+', '/', '=', whitespace, or a length that is not a multiple of 4 base64 chars. All earlier members (n, e, d, p, q, dp, dq) must decode successfully first, otherwise a different 'failed to decode ...' error is returned.
Common situations: Manually assembling a JWK from MPI/hex values produced by another crypto library without converting to base64url; truncating a long JWK line when copying; mixing up field order or pasting hex ('a1b2...') into qi; JWK tools that emit padded base64.
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 second exponent: %w
- failed to decode modulus: %w
- failed to decode exponent: %w
- failed to decode private exponent: %w
- failed to decode first prime factor: %w
AI-assisted analysis of grafana/k6@93accf6570 (2026-08-15).
Data as JSON: /api/errors/941cf24e1e28bee3.
Report an issue: GitHub.