Common causes
The token is simply old
Access tokens are short-lived on purpose. This one was issued at 2025-12-31T23:00:00Z and expired an hour later, at 2026-01-01T00:00:00Z.
Input
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzY3MjIyMDAwLCJleHAiOjE3NjcyMjU2MDB9.c2ln
JWT Decoder says
Expired. exp = 2026-01-01T00:00:00.000Z
The clocks disagree
A token that expires in 60 seconds counts as expired for a server whose clock runs two minutes ahead, while a client with the correct time still sees it as valid. Compare exp with the clock of the machine that rejected the token.
An old token is still being sent
The app refreshed the token but a cache, a stored header or a second tab still sends the previous one. The decoder shows which exp the request really carried.
How to fix it
- Decode the token and read exp and iat. They are seconds since 1970, not milliseconds.
- Compare exp with the current time on the machine that rejected it. Check that its clock is synchronised (NTP).
- Refresh the token before it expires, not after the first 401.
- Allow a few seconds of clock tolerance on the verifying side if your library supports it.