{"record":{"id":"579b58cef2171091","repo":"grafana/k6","slug":"invalid-rsa-jwk-key-w","errorCode":null,"errorMessage":"invalid RSA JWK key: %w","messagePattern":"invalid RSA JWK key: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":331,"sourceCode":"\t\treturn errors.New(\"modulus (n) is required\")\n\t}\n\n\tif jwk.E == \"\" {\n\t\treturn errors.New(\"exponent (e) is required\")\n\t}\n\n\t// TODO: consider validating the other fields in future\n\treturn nil\n}\n\nfunc importRSAJWK(jsonKeyData []byte) (any, CryptoKeyType, int, error) {\n\tvar jwk rsaJWK\n\tif err := json.Unmarshal(jsonKeyData, &jwk); err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to parse input as RSA JWK key: %w\", err)\n\t}\n\n\tif err := jwk.validate(); err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"invalid RSA JWK key: %w\", err)\n\t}\n\n\t// decode the various key components\n\tnBytes, err := base64URLDecode(jwk.N)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode modulus: %w\", err)\n\t}\n\teBytes, err := base64URLDecode(jwk.E)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, 0, fmt.Errorf(\"failed to decode exponent: %w\", err)\n\t}\n\n\t// convert exponent to an integer\n\teInt := new(big.Int).SetBytes(eBytes).Int64()\n\tpubKey := rsa.PublicKey{\n\t\tN: new(big.Int).SetBytes(nBytes),\n\t\tE: int(eInt),\n\t}","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L313-L349","documentation":"After parsing, importRSAJWK runs rsaJWK.validate(), which requires kty == 'RSA' plus non-empty n (modulus) and e (exponent); other private fields are deliberately not validated yet (see the TODO in the source). This error wraps any of those failures and is returned from crypto.subtle.importKey('jwk', ...) for RSA algorithms. The wrapped text identifies the failing field ('invalid key type: ...', 'modulus (n) is required', 'exponent (e) is required').","triggerScenarios":"RSA JWK import with kty not exactly 'RSA'; missing or empty n; missing or empty e. Present but malformed d/p/q/dp/dq/qi do not trigger this error (they fail later during decode or key validation).","commonSituations":"Public JWKs truncated during copying (missing e); JWKs exported with only kty and n; feeding JWT header fragments (no key material) to importKey.","solutions":["Ensure kty is 'RSA' and both n and e are present non-empty strings","Read the wrapped message to identify the missing field","Re-export the full JWK from the source key","Verify the JWK was not truncated by config templating or environment variables"],"exampleFix":"// before\nconst jwk = { kty: 'RSA', n: '...' }; // exponent missing\n// after\nconst jwk = { kty: 'RSA', n: '...', e: 'AQAB' };","handlingStrategy":"validation","validationCode":"function isValidRsaPublicJwk(jwk) {\n  return jwk && typeof jwk === 'object' &&\n    jwk.kty === 'RSA' &&\n    typeof jwk.n === 'string' && jwk.n !== '' &&\n    typeof jwk.e === 'string' && jwk.e !== '';\n}\nif (!isValidRsaPublicJwk(jwk)) throw new Error('RSA JWK needs kty=\"RSA\" plus non-empty n and e');","typeGuard":"function isCompleteRsaJwk(jwk) {\n  return !!jwk && typeof jwk === 'object' && jwk.kty === 'RSA' &&\n    jwk.n && jwk.e && (jwk.d === undefined || (jwk.p && jwk.q && jwk.dp && jwk.dq && jwk.qi));\n}","tryCatchPattern":"try {\n  key = await crypto.subtle.importKey('jwk', jwk, rsaAlg, true, usages);\n} catch (e) {\n  if (e.message.includes('invalid RSA JWK key')) {\n    throw new Error(`RSA JWK invalid: kty=${jwk.kty} n=${!!jwk.n} e=${!!jwk.e}`);\n  }\n  throw e;\n}","preventionTips":["Require kty, n, e in fixture linting before test runs","If d is present, require the full private field set to avoid later failures","Do not feed JWT headers or partial keys to importKey"],"tags":["webcrypto","jwk","import","rsa","validation","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}