{"record":{"id":"8df931a744473bab","repo":"grafana/k6","slug":"failed-to-convert-ecdh-key-to-ecdsa-key-w","errorCode":null,"errorMessage":"failed to convert ECDH key to ECDSA key: %w","messagePattern":"failed to convert ECDH key to ECDSA key: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":188,"sourceCode":"\texported.Set(\"kty\", JWKECKeyType)\n\n\tvar x, y, d *big.Int\n\tvar curveParams *elliptic.CurveParams\n\n\tswitch k := key.handle.(type) {\n\tcase *ecdsa.PrivateKey:\n\t\tx = k.X\n\t\ty = k.Y\n\t\td = k.D\n\t\tcurveParams = k.Params()\n\tcase *ecdsa.PublicKey:\n\t\tx = k.X\n\t\ty = k.Y\n\t\tcurveParams = k.Params()\n\tcase *ecdh.PrivateKey:\n\t\tecdsaKey, err := convertECDHtoECDSAKey(k)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to convert ECDH key to ECDSA key: %w\", err)\n\t\t}\n\n\t\tx = ecdsaKey.X\n\t\ty = ecdsaKey.Y\n\t\td = ecdsaKey.D\n\t\tcurveParams = ecdsaKey.Params()\n\tcase *ecdh.PublicKey:\n\t\tecdsaKey, err := convertPublicECDHtoECDSA(k)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to convert ECDH key to ECDSA key: %w\", err)\n\t\t}\n\n\t\tx = ecdsaKey.X\n\t\ty = ecdsaKey.Y\n\t\tcurveParams = ecdsaKey.Params()\n\tdefault:\n\t\treturn nil, errors.New(\"key's handle isn't an ECDSA/ECDH public/private key\")\n\t}","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L170-L206","documentation":"k6's WebCrypto implementation stores ECDH keys internally as Go *ecdh.PrivateKey handles, but the JWK exporter (exportECJWK in internal/js/modules/k6/webcrypto/jwk.go) only serializes ECDSA-shaped keys, so it first converts the ECDH key via convertECDHtoECDSAKey. This error means that conversion failed, and it is raised from crypto.subtle.exportKey('jwk', key). The underlying cause is either an unsupported curve (only P-256/P-384/P-521 map to ECDSA curves) or a public point that cannot be unmarshaled onto the ECDSA curve.","triggerScenarios":"Calling crypto.subtle.exportKey('jwk', ecdhPrivateKey) where the key handle's curve is not P-256, P-384 or P-521, or where the stored key bytes are not a valid point on the curve (corrupted handle). The error is wrapped around the converter's own error ('curve not supported for converting to ECDSA key' or 'unable to convert ECDH public key to ECDSA public key').","commonSituations":"Exporting an ECDH key that was generated or imported outside the supported NIST curves; keys whose material was mutated between import and export; edge-case k6 versions where the ECDH handle was constructed inconsistently. Rare in practice because import paths validate the point, so hitting it usually indicates an internal inconsistency.","solutions":["Confirm the key was generated/imported with namedCurve 'P-256', 'P-384' or 'P-521'","Re-generate the key pair with crypto.subtle.generateKey({name:'ECDH', namedCurve:'P-256'}, ...) and export the new key","Fall back to exporting in 'raw' (public), 'spki' (public) or 'pkcs8' (private) format, which do not use the ECDSA conversion","If the key came from generateKey with a supported curve, report it as a k6 bug at https://github.com/grafana/k6/issues including the curve and import path used"],"exampleFix":"// before\nconst jwk = await crypto.subtle.exportKey('jwk', ecdhPrivKey); // throws: failed to convert ECDH key to ECDSA key\n\n// after\nconst raw = new Uint8Array(await crypto.subtle.exportKey('pkcs8', ecdhPrivKey));\n// or regenerate with a supported curve:\nconst pair = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey', 'deriveBits']);\nconst jwk2 = await crypto.subtle.exportKey('jwk', pair.privateKey);","handlingStrategy":"try-catch","validationCode":"const SUPPORTED = ['P-256', 'P-384', 'P-521'];\nfunction canExportEcdhJwk(key) {\n  return key.algorithm && key.algorithm.name === 'ECDH' &&\n         SUPPORTED.includes(key.algorithm.namedCurve);\n}","typeGuard":"function isExportableEcdhKey(key) {\n  return key && key.algorithm && key.algorithm.name === 'ECDH' &&\n    ['P-256', 'P-384', 'P-521'].includes(key.algorithm.namedCurve) &&\n    typeof key.extract === 'function';\n}","tryCatchPattern":"try {\n  const jwk = await crypto.subtle.exportKey('jwk', ecdhPriv);\n} catch (e) {\n  if (e.message.includes('failed to convert ECDH key to ECDSA key')) {\n    const pkcs8 = await crypto.subtle.exportKey('pkcs8', ecdhPriv); // fallback format\n  } else { throw e; }\n}","preventionTips":["Generate ECDH keys only with namedCurve P-256/P-384/P-521","Set extractable=true at generateKey/importKey time if you plan to export as JWK","Round-trip test: export a freshly generated key as Jwk once in a sanity check"],"tags":["webcrypto","jwk","export","ecdh","elliptic-curve","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}