JWT Token Expired Error — Causes, Detection, and Fix
💡A JWT token expired error means the token's exp claim is in the past. JWTs have a built-in expiry timestamp in the payload. When a request reaches the server with an expired token, authentication fails with 401. Fix: implement a refresh token flow to get a new access token before the old one expires. Decode the JWT to inspect its expiry with the JWT Decoder.
You may also see this as:
JsonWebTokenError: jwt expiredTokenExpiredError: jwt expired401 Unauthorized: Token has expiredJWT verification failed: token is expiredexp claim is in the past
Quick Diagnosis
If you see “TokenExpiredError: jwt expired” → it means the token's exp timestamp is before the current time → do this: check the exp value in the JWT payload — use a JWT decoder to read it
If you see “401 on all requests after some time” → it means access token has expired and no refresh logic is in place → do this: implement refresh token flow — request new access token using refresh token
If you see “token valid locally but expired on server” → it means server and client clocks are out of sync → do this: check server time — JWT exp uses Unix timestamp, clock drift causes false expiry
If you see “token expired immediately after login” → it means exp is set too short or there's a clock mismatch → do this: decode the token — check iat (issued at) and exp difference
Quick Fix — Step by Step
- Decode the JWT and check the exp field (Unix timestamp)
- Compare exp with current time: exp * 1000 vs Date.now()
- Implement refresh token logic — request new access token before expiry
- Add an interceptor to catch 401 responses and refresh automatically
- Increase token TTL if expiry is too short for your use case
Inspect Your Token
Common Causes and Fixes
Read exp from JWT payload
❌ Wrong
const isExpired = token.exp < Date.now();✅ Fixed
const payload = JSON.parse(atob(token.split('.')[1]));
const isExpired = payload.exp * 1000 < Date.now();JWT exp is in seconds, Date.now() is milliseconds. Multiply exp by 1000 to compare.
No refresh handling
❌ Wrong
if (error.status === 401) {
logout();
}✅ Fixed
if (error.status === 401 && !error.config._retry) {
error.config._retry = true;
const newToken = await refreshAccessToken();
return retryWithNewToken(error.config, newToken);
}Logging out on every 401 is bad UX. Try refreshing first, then logout if refresh fails.
Wrong exp comparison
❌ Wrong
if (new Date(payload.exp) < new Date()) { /* expired */ }✅ Fixed
if (payload.exp * 1000 < Date.now()) { /* expired */ }new Date(payload.exp) treats exp as milliseconds. JWT exp is Unix seconds — multiply by 1000.
Verify with expiry handling in Node
❌ Wrong
const payload = jwt.decode(token); // doesn't verify!✅ Fixed
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
} catch(e) {
if (e instanceof jwt.TokenExpiredError) {
// handle specifically
}
}jwt.decode() skips verification. Always use jwt.verify() on the server.
Real-World Context
Check JWT expiry before request
function isTokenExpired(token) {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now();
}Proactively check before each request to avoid sending expired tokens.
Axios interceptor for auto-refresh
axios.interceptors.response.use(
r => r,
async error => {
if (error.response?.status === 401) {
const newToken = await refreshAccessToken();
error.config.headers.Authorization = `Bearer ${newToken}`;
return axios(error.config);
}
return Promise.reject(error);
}
);Intercept 401 responses, refresh the token, and retry the original request transparently.
Proactive token refresh
const REFRESH_BUFFER = 5 * 60 * 1000; // 5 min
const payload = JSON.parse(atob(token.split('.')[1]));
const expiresAt = payload.exp * 1000;
if (expiresAt - Date.now() < REFRESH_BUFFER) {
await refreshToken();
}Refresh 5 minutes before expiry to avoid requests failing mid-flight.
Related Guides
Frequently Asked Questions
How long should a JWT token last?
Access tokens: 15 minutes to 1 hour. Refresh tokens: 7-30 days. Short-lived access tokens reduce risk if a token is leaked.
Can I extend a JWT token's expiry?
No. JWTs are immutable — the exp is signed into the token. To extend, issue a new token with a new expiry using a refresh token.
How do I invalidate a JWT before it expires?
JWTs are stateless — you can't invalidate them without a blocklist. Store invalidated token IDs in a fast store like Redis and check on each request.
All tools run in your browser. Your data never leaves your device.