You called something like jwt.decode(token) or jwt_decode.decode(token) and got TypeError: jwt.decode is not a function. The cause is almost always a mix-up between two different libraries that both deal with JWTs, and the fix is quick. But this error tends to show up right next to a security mistake, so it is worth understanding both.
The quick fix
The jwt-decode library exports the decode function itself, not an object with a .decode() method. In current versions it is a named export:
// jwt-decode (browser) — reads the payload, does NOT verify
import { jwtDecode } from "jwt-decode";
const payload = jwtDecode(token); // not jwtDecode.decode(token)
// jsonwebtoken (server) — this one DOES have .decode() and .verify()
import jwt from "jsonwebtoken";
const claims = jwt.verify(token, process.env.JWT_SECRET);If you were calling .decode(), you were likely using jwt-decode as if it were jsonwebtoken. Pick the one that matches where your code runs: jwt-decode in the browser to read a claim for display, jsonwebtoken on the server to actually trust the token.
Want to see exactly what Prbl flags? Watch it scan a demo app, no repo or account needed.
See a live scan →The security bug this often hides
Here is why the error is worth pausing on. People usually hit it while trying to read a value out of a token, a user id, a role, an isAdmin flag, to decide what the user is allowed to do. And decoding, with either library, only reads the payload. It does not check that the token is genuine. Anyone can craft a token with any claims they want, so trusting a decoded field for an authorization decision is a real vulnerability.
If the token is being used to make a trust decision, you must verify it on the server, which checks the signature against your secret, not just decode it. The full explanation is in JWT decode vs verify, and the concept behind it is in what a JWT is.
The rule of thumb
- Reading a claim for display in the browser —
jwtDecode(token)from jwt-decode is fine. Never trust the result for access control. - Making an authorization decision —
jwt.verify(token, secret)from jsonwebtoken, on the server, every time. - If you are not sure which you are doing — you are probably decoding when you should be verifying. Assume verify.
Frequently asked questions
Why do I get 'jwt.decode is not a function'?
Almost always because you imported the jwt-decode library as if it were an object with a .decode method, when it exports the decode function itself. In older versions you wrote `import jwt_decode from 'jwt-decode'` and called `jwt_decode(token)`; in newer versions it is a named export, `import { jwtDecode } from 'jwt-decode'`, called as `jwtDecode(token)`. Calling `.decode()` on it fails because there is no such method. The other common cause is confusing jwt-decode with jsonwebtoken, which does have `jwt.decode()` and `jwt.verify()`.
What's the difference between jwt-decode and jsonwebtoken?
jwt-decode is a tiny browser library that only reads the payload; it cannot verify a signature because that needs your secret, which never belongs in the browser. jsonwebtoken is a server library that can both decode and, importantly, verify. If you are on the server and need to trust the token, use jsonwebtoken's verify. If you are in the browser and only want to read a claim for display, jwt-decode is fine, but never trust what it returns for an access decision.
So which import should I use?
In the browser, `import { jwtDecode } from 'jwt-decode'` and call `jwtDecode(token)` to read the payload for display only. On the server, `import jwt from 'jsonwebtoken'` and call `jwt.verify(token, secret)` to actually trust the token. The error you hit is a signal to check which library you are using and, more importantly, whether you are verifying or just decoding.
Why does this error matter for security?
Because the fix people reach for often leaves the real bug in place. If you were trying to read a user's role or id from a token to make an authorization decision, decoding it, however you spell the call, is not enough. Anyone can hand you a token with any claims they like. You have to verify the signature on the server before you trust a single field. The error is a good moment to ask whether you are decoding when you should be verifying.
Check your token handling
Decoding a token instead of verifying it is one of the patterns we look for, because it passes every functional test and only fails when someone forges a token. A scan reads the repo and flags a decode used where a verify belongs. Run a free scan and see whether your app trusts a token it never verified.