Error message
DataError: Data provided to an operation does not meet requirements
The DOMException name DataError is what to match on; the message text varies by runtime. Node prints "Invalid keyData" (or a specific reason like "Named curve mismatch"); browsers typically show the generic sentence above.
WebCrypto crypto.subtle.importKey — browsers & Node
What it means
importKey (or unwrapKey) rejected the key bytes: they do not parse as the format you declared, or their contents contradict the algorithm parameters (wrong curve, mismatched JWK fields). It is the WebCrypto equivalent of "these bytes are not the key you say they are" — thrown before any key object is created.
Why it happens
Format name does not match the bytes
commonImporting a public key with format "pkcs8" (private-key container) or a private key with "spki" is the classic swap. The names are unintuitive: spki = public, pkcs8 = private, raw = bare EC point, jwk = JSON object.
PEM text was not converted to binary
commonimportKey takes an ArrayBuffer of DER, not PEM text. Passing the PEM string encoded as UTF-8 — or base64-decoding it without first stripping the BEGIN/END lines and newlines — produces bytes that fail structural parsing.
Curve mismatch between key and algorithm
occasionalA P-256 SPKI imported with namedCurve: "P-384" (Node reports "Named curve mismatch"), or a JWK whose crv disagrees with the requested algorithm. The bytes are fine; the declaration is wrong.
Invalid raw point
occasionalFor format "raw", the 65 bytes must be 0x04 ‖ X ‖ Y with (X, Y) actually on the curve. A corrupted byte or a compressed point (0x02/0x03 prefix, unsupported in most runtimes) fails import.
How to fix it
- 1.
Convert PEM → ArrayBuffer correctly
Strip the armor lines, base64-decode the body, and pick the format by the PEM label: PUBLIC KEY → spki, PRIVATE KEY → pkcs8.
js function pemToArrayBuffer(pem) { const b64 = pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, ""); return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)).buffer; } const key = await crypto.subtle.importKey( "spki", pemToArrayBuffer(publicKeyPem), { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"], ); - 2.
Prefer JWK when you have JSON key material
JWKS endpoints already serve WebCrypto-ready objects — no binary conversion, and crv travels with the key.
js const key = await crypto.subtle.importKey( "jwk", jwk, { name: "ECDSA", namedCurve: jwk.crv }, false, ["verify"], ); - 3.
Confirm what the bytes really are
Paste the key into the certificate decoder or signature verifier below — both parse SPKI/PKCS#8/JWK locally and report the actual curve and key type, which usually names the mismatch immediately.