Error message
Key for the ES256 algorithm must be one of type CryptoKey, KeyObject, or JSON Web Key.
Also printed as
- Key for the RS256 algorithm must be one of type CryptoKey, KeyObject, or JSON Web Key.
- Key for the HS256 algorithm must be one of type CryptoKey, KeyObject, JSON Web Key, or Uint8Array.
The algorithm in the sentence follows your token (`ES256`, `RS256`, `EdDSA`…). For HMAC algorithms the list ends with "or Uint8Array".
jose (JavaScript) — TypeError from jwtVerify / SignJWT.sign
What it means
You passed jose a value it cannot use as a key — most often a PEM string or a plain string secret. Since version 4, jose accepts only key objects: a WebCrypto `CryptoKey`, a Node.js `KeyObject`, a JWK object, or (for HMAC algorithms) a `Uint8Array`. Text formats must be imported first; jose never guesses what a string contains.
Why it happens
A PEM string was passed directly
commonCode migrated from jsonwebtoken, which accepts PEM text, often passes `process.env.PUBLIC_KEY` straight to `jwtVerify`. jose requires `importSPKI` (public keys), `importPKCS8` (private keys) or `importX509` (certificates) first.
An HMAC secret was passed as a string
commonFor HS256/HS384/HS512 the secret must be bytes: `new TextEncoder().encode(secret)`. The string itself is rejected even though it is the right secret.
A JWKS URL string instead of a key set function
occasional`"https://issuer/.well-known/jwks.json"` is a string, not a key. Wrap it with `createRemoteJWKSet(new URL(...))`, which returns the function `jwtVerify` expects.
The configuration value is missing
occasionalAn unset environment variable gives `undefined`, which fails the same type check. Validate configuration at startup so the error names the missing variable instead.
How to fix it
- 1.
Import PEM keys once, at startup
`importSPKI` and `importPKCS8` take the PEM text and the algorithm the key will be used with, and return a `CryptoKey` you can reuse for every request.
js import { importSPKI, importPKCS8, jwtVerify } from "jose"; const publicKey = await importSPKI(process.env.JWT_PUBLIC_KEY_PEM, "ES256"); // -----BEGIN PUBLIC KEY----- await jwtVerify(token, publicKey, { algorithms: ["ES256"] }); const privateKey = await importPKCS8(process.env.JWT_PRIVATE_KEY_PEM, "ES256"); // -----BEGIN PRIVATE KEY----- - 2.
Encode HMAC secrets as bytes
Use a random secret of at least 32 bytes for HS256 and pass it as a `Uint8Array`. If the secret is stored base64-encoded, decode it to bytes instead of encoding the base64 text.
js import { jwtVerify } from "jose"; const secret = new TextEncoder().encode(process.env.JWT_SECRET); // 32+ random bytes for HS256 await jwtVerify(token, secret, { algorithms: ["HS256"] }); - 3.
Verify against a key set, not a URL
For tokens from an identity provider, pass the function returned by `createRemoteJWKSet`; jose then picks the key by `kid` for each token.
js import { createRemoteJWKSet, jwtVerify } from "jose"; const jwks = createRemoteJWKSet(new URL("https://issuer.example.com/.well-known/jwks.json")); await jwtVerify(token, jwks, { algorithms: ["ES256"] });