{"record":{"id":"50a4284814e41075","repo":"grafana/k6","slug":"failed-to-parse-input-as-ecdh-key-w","errorCode":null,"errorMessage":"failed to parse input as ECDH key: %w","messagePattern":"failed to parse input as ECDH key: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/js/modules/k6/webcrypto/jwk.go","lineNumber":272,"sourceCode":"\t\treturn pk, PublicCryptoKeyType, nil\n\t}\n\n\td, err := base64URLDecode(jwkKey.D)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, fmt.Errorf(\"failed to decode D: %w\", err)\n\t}\n\n\treturn &ecdsa.PrivateKey{\n\t\tPublicKey: *pk,\n\t\tD:         new(big.Int).SetBytes(d),\n\t}, PrivateCryptoKeyType, nil\n}\n\nfunc importECDHJWK(_ EllipticCurveKind, jsonKeyData []byte) (any, CryptoKeyType, error) {\n\t// first we do try to parse the key as ECDSA key\n\tkey, _, err := importECDSAJWK(EllipticCurveKindP256, jsonKeyData)\n\tif err != nil {\n\t\treturn nil, UnknownCryptoKeyType, fmt.Errorf(\"failed to parse input as ECDH key: %w\", err)\n\t}\n\n\tswitch key := key.(type) {\n\tcase *ecdsa.PrivateKey:\n\t\tecdhKey, err := key.ECDH()\n\t\tif err != nil {\n\t\t\treturn nil, UnknownCryptoKeyType, fmt.Errorf(\"failed to convert ECDSA key to ECDH key: %w\", err)\n\t\t}\n\n\t\treturn ecdhKey, PrivateCryptoKeyType, nil\n\tcase *ecdsa.PublicKey:\n\t\tecdhKey, err := key.ECDH()\n\t\tif err != nil {\n\t\t\treturn nil, UnknownCryptoKeyType, fmt.Errorf(\"failed to convert ECDSA key to ECDH key: %w\", err)\n\t\t}\n\n\t\treturn ecdhKey, PublicCryptoKeyType, nil\n\tdefault:","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/grafana/k6/blob/93accf6570dcd306ca5e99cc44c393ee3797761b/internal/js/modules/k6/webcrypto/jwk.go#L254-L290","documentation":"importECDHJWK implements ECDH JWK import by first running the same ECDSA JWK parser, then converting the result to an ECDH key. This error is the wrapper around any failure of that first parsing stage, so its text ('failed to parse input as ECDH key') is generic; the specific cause (bad JSON, missing fields, bad crv, bad base64url) is in the wrapped %w chain. It is raised from crypto.subtle.importKey('jwk', ..., {name:'ECDH', namedCurve:...}).","triggerScenarios":"Any of the ECDSA-parse failures occurring while importing with the ECDH algorithm: non-string fields, kty != 'EC', missing crv/x/y, crv outside P-256/P-384/P-521, or x/y/d not in unpadded base64url.","commonSituations":"Same real-world causes as the ECDSA parse errors (double-encoded JSON, padded base64url, non-canonical curve names), but seen through the ECDH code path where the generic message can mask which field is at fault.","solutions":["Read the wrapped cause after 'failed to parse input as ECDH key:' to find the real failure","Validate the JWK shape: kty 'EC', crv one of P-256/P-384/P-521, x/y present as unpadded base64url strings","Pass the JWK as an object, not a JSON string","Test the same JWK with {name:'ECDSA'} import to get the more specific error message, then fix that field"],"exampleFix":"// before\nconst key = await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: 'P-256' }, true, []); // generic failure\n// after\n// diagnose with the ECDSA importer to get the precise field error, fix it, then import as ECDH:\ntry { await crypto.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, true, []); } catch (e) { console.log(e.message); }\nconst key = await crypto.subtle.importKey('jwk', fixedJwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey', 'deriveBits']);","handlingStrategy":"try-catch","validationCode":"const B64URL = /^[A-Za-z0-9_-]+$/;\nfunction isValidEcdhJwk(j) {\n  return j && typeof j === 'object' && j.kty === 'EC' &&\n    ['P-256','P-384','P-521'].includes(j.crv) &&\n    typeof j.x === 'string' && B64URL.test(j.x) &&\n    typeof j.y === 'string' && B64URL.test(j.y) &&\n    (j.d === undefined || (typeof j.d === 'string' && B64URL.test(j.d)));\n}","typeGuard":"function isEcdhImportableJwk(j) {\n  return !!j && typeof j === 'object' && j.kty === 'EC' &&\n    ['P-256','P-384','P-521'].includes(j.crv) &&\n    /^[A-Za-z0-9_-]+$/.test(j.x || '') && /^[A-Za-z0-9_-]+$/.test(j.y || '');\n}","tryCatchPattern":"try {\n  key = await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: jwk.crv }, true, ['deriveKey','deriveBits']);\n} catch (e) {\n  if (e.message.includes('failed to parse input as ECDH key')) {\n    // wrapper hides the cause: re-run via the ECDSA importer for the specific field error\n    try { await crypto.subtle.importKey('jwk', jwk, { name: 'ECDSA', namedCurve: 'P-256' }, true, []); }\n    catch (e2) { throw new Error('ECDH JWK root cause: ' + e2.message); }\n  }\n  throw e;\n}","preventionTips":["Remember ECDH JWK import reuses the ECDSA parser — fix everything the ECDSA path complains about","Pre-validate the full EC JWK shape (kty, crv, x, y, optional d)","Pass namedCurve consistent with the JWK's crv"],"tags":["webcrypto","jwk","import","ecdh","error-wrapping","k6"],"backgroundTag":null,"analyzedSha":"93accf6570dcd306ca5e99cc44c393ee3797761b","analyzedAt":"2026-08-15T21:23:27.118Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}