{"record":{"id":"e2300410a67fa74c","repo":"grafana/k6","slug":"failed-to-decode-second-exponent-w","errorCode":null,"errorMessage":"failed to decode second exponent: %w","messagePattern":"failed to decode second exponent: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":374,"sourceCode":"\tdBytes, err := base64URLDecode(jwk.D)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode private exponent: %w\", err)\n\t}\n\tpBytes, err := base64URLDecode(jwk.P)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode first prime factor: %w\", err)\n\t}\n\tqBytes, err := base64URLDecode(jwk.Q)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode second prime factor: %w\", err)\n\t}\n\tdpBytes, err := base64URLDecode(jwk.Dp)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode first exponent: %w\", err)\n\t}\n\tdqBytes, err := base64URLDecode(jwk.Dq)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode second exponent: %w\", err)\n\t}\n\tqiBytes, err := base64URLDecode(jwk.Qi)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode coefficient: %w\", err)\n\t}\n\n\tprivKey := &rsa.PrivateKey{\n\t\tPublicKey: pubKey,\n\t\tD:         new(big.Int).SetBytes(dBytes),\n\t\tPrimes: []*big.Int{\n\t\t\tnew(big.Int).SetBytes(pBytes),\n\t\t\tnew(big.Int).SetBytes(qBytes),\n\t\t},\n\t\tPrecomputed: rsa.PrecomputedValues{\n\t\t\tDp:   new(big.Int).SetBytes(dpBytes),\n\t\t\tDq:   new(big.Int).SetBytes(dqBytes),\n\t\t\tQinv: new(big.Int).SetBytes(qiBytes),\n\t\t},","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L356-L392","documentation":"Thrown by k6's webcrypto RSA JWK importer. When importing an RSA private key with crypto.subtle.importKey('jwk', ...), k6 base64url-decodes every CRT field; this error means the 'dq' (second exponent / d mod q-1) member is not valid unpadded base64url. The underlying decode error is wrapped, so the message ends with the exact base64 failure reason.","triggerScenarios":"Calling crypto.subtle.importKey('jwk', jwk, {name:'RSA-OAEP'|'RSA-PSS'|'RSASSA-PKCS1-v1_5', ...}, ...) where jwk.dq contains characters outside the base64url alphabet (e.g. '+', '/', or non-ASCII) or has the wrong length/padding ('=' is not accepted). Fields n, e, d, p, q, and dp must already decode cleanly or an earlier error fires instead.","commonSituations":"Hand-editing or copy-pasting a JWK and corrupting the dq value; converting a JWK from standard base64 to base64url but leaving '+'/'/' or '=' in place; generating the JWK with a tool that emits padded standard base64 instead of RFC 7515 base64url; passing a public-only JWK where dq is garbage rather than empty.","solutions":["Regenerate the whole JWK pair with crypto.subtle.generateKey and crypto.subtle.exportKey('jwk', ...) so all CRT members are consistently encoded","Re-encode dq as unpadded base64url: strip '=', then translate '+'->'-' and '/'->'_'","Verify each JWK member decodes before import (see validation snippet) and print which field fails","If the key material is unreliable, import from PEM/PKCS8/SPKI instead of JWK"],"exampleFix":"// before\nconst key = await crypto.subtle.importKey('jwk', jwk, alg, true, ['decrypt']);\n// jwk.dq was standard base64: 'AbC+d/...='\n\n// after: normalize every member to unpadded base64url\nconst b64url = s => s.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\nconst clean = { ...jwk, dq: b64url(jwk.dq) };\nconst key = await crypto.subtle.importKey('jwk', clean, alg, true, ['decrypt']);","handlingStrategy":"validation","validationCode":"const B64URL_RE = /^[A-Za-z0-9_-]*$/;\nfunction assertValidRsaJwk(jwk) {\n  for (const f of ['n','e','d','p','q','dp','dq','qi']) {\n    const v = jwk[f];\n    if (typeof v !== 'string' || !B64URL_RE.test(v)) {\n      throw new Error(`JWK member '${f}' is not unpadded base64url`);\n    }\n  }\n}\nassertValidRsaJwk(jwk);\nawait crypto.subtle.importKey('jwk', jwk, alg, true, usages);","typeGuard":"const isB64Url = s => typeof s === 'string' && /^[A-Za-z0-9_-]*$/.test(s);\nconst hasValidRsaCrt = j => ['n','e','d','p','q','dp','dq','qi'].every(k => isB64Url(j[k]));","tryCatchPattern":"try { return await crypto.subtle.importKey('jwk', jwk, alg, true, usages); }\ncatch (e) { throw new Error(`bad JWK (${e.message}); re-export from a trusted key`); }","preventionTips":["Always generate JWKs via crypto.subtle.exportKey rather than assembling them","Store keys in their original encoded form; never round-trip through editors that alter base64","Unit-test your key fixtures once at script start so failures are loud and early","Prefer PKCS8/SPKI PEM import when the key crosses systems"],"tags":["webcrypto","rsa","jwk","base64url","importkey"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}