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 →

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 are approval-gated and 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 config changes approval-gated and recorded

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.