Error message
TypeError: Failed to execute 'importKey' on 'SubtleCrypto': The provided value is not of type '(ArrayBuffer or ArrayBufferView or JsonWebKey)'.
Also printed as
- TypeError: SubtleCrypto.importKey: Argument 2 is not an object.
- TypeError: Type error
Chromium's text is above; Firefox says "SubtleCrypto.importKey: Argument 2 is not an object.", Safari/WebKit just "Type error". Node.js reports `ERR_INVALID_ARG_TYPE` for the same call.
WebCrypto SubtleCrypto.importKey — PEM text passed as keyData (Chromium / Firefox / WebKit)
What it means
`importKey` received a string as `keyData`. WebCrypto never parses text: for `"spki"`, `"pkcs8"` and `"raw"` it wants binary DER (an `ArrayBuffer` or typed array), for `"jwk"` a JavaScript object. The browser rejects the call during argument conversion, before looking at the format or the key — so the PEM may be perfectly valid.
Why it happens
A PEM string passed directly
commonThe `-----BEGIN PUBLIC KEY-----` text from a server, a config file or a JWT library's documentation. It has to be base64-decoded to DER first.
Base64 text instead of bytes
commonEven without the armour lines, a base64 string is still a string. Decode it with `atob` (or `Uint8Array.fromBase64` where available) into bytes.
A JWK as JSON text
occasionalFor format `"jwk"`, `JSON.stringify`-ed text fails the same way; pass the parsed object.
How to fix it
- 1.
Strip the armour and decode to bytes
Remove the BEGIN/END lines and whitespace, base64-decode, and choose the format by label: `PUBLIC KEY` → `spki`, `PRIVATE KEY` → `pkcs8`.
js function pemToDer(pem) { const base64 = pem.replace(/-----(BEGIN|END) [A-Z ]+-----/g, "").replace(/\s+/g, ""); return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); } const publicKey = await crypto.subtle.importKey( "spki", pemToDer(publicKeyPem), // "PUBLIC KEY" -> spki, "PRIVATE KEY" -> pkcs8 { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"], ); - 2.
Pass JWKs as objects
JWKS endpoints already deliver objects; parse JSON text once and pass the object with the matching curve.
js const jwk = JSON.parse(jwkText); // an object, not the JSON string const key = await crypto.subtle.importKey("jwk", jwk, { name: "ECDSA", namedCurve: jwk.crv }, false, ["verify"]);