Deploy Error Decoder

JWT verify errors — expired, malformed, invalid signature

Quick answer: the jsonwebtoken library throws a different error per cause: jwt expired means the exp claim has passed (issue a fresh token), jwt malformed means the value is not a valid 3-part token (often the Bearer prefix or undefined), and invalid signature means the secret or key does not match what signed it. Fix each by its cause - they are not the same bug.

Not your exact error? Paste it into the Deploy Error Decoder →
On this page

What the errors look like

Each is a distinct error name from jwt.verify() - the name tells you the cause:

TokenExpiredError: jwt expired
JsonWebTokenError: jwt malformed
JsonWebTokenError: invalid signature
JsonWebTokenError: invalid algorithm

Do not treat them as one generic “auth failed.” expired is a valid token past its time; malformed is not a token at all; invalid signature is a real token signed by a different key.

Why each one happens

jwt expired

The exp claim has passed - the lifetime was short, or server clocks are skewed.

jwt malformed

The value is not a 3-part token - usually the 'Bearer ' prefix was not stripped, or it is undefined.

invalid signature

Verified with a different secret/key than signed it - an env mismatch across instances, or a rotated key.

invalid algorithm

Signing and verifying disagree on the algorithm - e.g. HS256 vs RS256.

Diagnose it in three steps

1

Decode (do not verify) to inspect

const jwt = require('jsonwebtoken');
console.log(jwt.decode(token, { complete: true }));
// read exp, alg, iss - or paste it into a JWT inspector.
2

Confirm you stripped 'Bearer '

const raw = req.headers.authorization || '';
const token = raw.replace(/^Bearer /, '');   // a top 'malformed' cause
console.log(JSON.stringify(token));
3

Check the secret and algorithm match

# same JWT_SECRET on the signing and verifying side?
# same algorithm? pin it on verify so a mismatch is explicit.
The real fix

Strip Bearer, pin the algorithm, match the secret

Verify the raw token (no prefix) with the same secret and a pinned algorithm, and handle expiry as its own case so the client can refresh instead of being logged out hard.

const jwt = require('jsonwebtoken');
const token = (req.headers.authorization || '').replace(/^Bearer /, '');
try {
  const payload = jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256'],   // pin it - never accept 'none'
    clockTolerance: 5,       // seconds, for minor clock skew
  });
  req.user = payload;
} catch (e) {
  if (e.name === 'TokenExpiredError') return res.status(401).json({ error: 'expired' });
  return res.status(401).json({ error: 'invalid token' });
}

If you see invalid signature only on some requests, you almost certainly have a different JWT_SECRET on different instances - make the secret identical everywhere it is verified.

How Infraveil handles this

One consistent secret across your fleet

Intermittent invalid signature failures are a config-consistency problem: one instance signs with a secret another does not share. On your own servers, Infraveil distributes and manages config and secrets consistently across the services you run, so every instance verifies with the same key - and changes use the configured manual, exact-hash allowlist, or automatic approval mode; selected operational events are recorded.

The same secret and config on every instance, on infra you control
Auth failures surfaced with request context in one view
Secret rotation and configuration changes use the configured manual, exact-hash allowlist, or automatic approval mode

Frequently asked questions

What does 'jwt expired' mean?

The token's exp claim is in the past, so verify throws TokenExpiredError. Issue a fresh token (via a refresh token) rather than extending lifetimes; a small clockTolerance absorbs minor clock skew.

What causes 'jwt malformed'?

The value passed to verify is not a valid three-part JWT - usually the 'Bearer ' prefix was not stripped, or it is undefined/empty. Log the exact value you verify.

What causes 'invalid signature'?

Verifying with a different secret or key (or algorithm) than signed the token - often a different JWT_SECRET on another instance, a rotated key, or HS256 vs RS256. Match both sides exactly.

How do I inspect a token safely?

Decode rather than verify - the header and payload are base64url JSON you can read without the secret. Use a JWT inspector to check exp/alg/claims, but never trust an unverified token for access.