{"record":{"id":"6e95feb33eb2c0cb","repo":"grafana/k6","slug":"failed-to-decode-first-exponent-w","errorCode":null,"errorMessage":"failed to decode first exponent: %w","messagePattern":"failed to decode first exponent: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":370,"sourceCode":"\tif jwk.D == \"\" {\n\t\treturn pubKey, PublicCryptoKeyType, pubKey.N.BitLen(), nil\n\t}\n\n\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{","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L352-L388","documentation":"During RSA private-key JWK import, the CRT exponent dp (JWK field 'dp', exponent1) is decoded with base64.RawURLEncoding. This error indicates dp is not valid unpadded base64url: padding, '+'/'/' characters, whitespace, or bad length. Fields dq and qi have identical checks immediately after, so fixing encoding once usually resolves all three.","triggerScenarios":"dp in padded or standard base64; dp truncated; whitespace inside dp; a JWK that includes d/p/q but omits or mangles dp while still declaring a private key.","commonSituations":"Private JWKs from providers that use padded base64url; partial JWKs where CRT parameters were dropped or corrupted; values round-tripped through systems that alter padding.","solutions":["Convert dp (and dq, qi) to unpadded base64url","Strip whitespace and '=' padding from all CRT fields","If CRT parameters are unavailable, note that k6's importer requires them for private RSA JWKs — obtain a complete JWK","Validate all fields in one pass with a base64url regex before calling importKey"],"exampleFix":"// before\nconst jwk = { kty: 'RSA', n, e: 'AQAB', d, p, q, dp: paddedDp, dq: paddedDq, qi: paddedQi };\n// after\nconst toB64u = (s) => s.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '').replace(/\\s+/g, '');\nconst jwk = { kty: 'RSA', n, e: 'AQAB', d, p, q, dp: toB64u(paddedDp), dq: toB64u(paddedDq), qi: toB64u(paddedQi) };","handlingStrategy":"validation","validationCode":"const B64URL = /^[A-Za-z0-9_-]+$/;\nfor (const f of ['dp', 'dq', 'qi']) {\n  if (jwk[f] !== undefined) {\n    const v = String(jwk[f]).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '').replace(/\\s+/g, '');\n    if (!B64URL.test(v)) throw new Error(f + ' is not unpadded base64url');\n    jwk[f] = v;\n  }\n}","typeGuard":"function hasB64uCrtParams(jwk) {\n  const ok = (s) => typeof s === 'string' && /^[A-Za-z0-9_-]+$/.test(s);\n  return ok(jwk.dp) && ok(jwk.dq) && ok(jwk.qi);\n}","tryCatchPattern":"try {\n  key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);\n} catch (e) {\n  if (e.message.includes('failed to decode first exponent')) {\n    jwk = { ...jwk, dp: toB64u(jwk.dp), dq: toB64u(jwk.dq), qi: toB64u(jwk.qi) };\n    key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);\n  } else throw e;\n}","preventionTips":["Validate dp, dq and qi together — they share encoding and provenance","Note that RSA private JWK import in k6 requires all CRT fields, unlike some libraries","Run a one-shot base64url normalization over the whole JWK before import"],"tags":["webcrypto","jwk","import","rsa","base64url","private-key","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}