Code Beautifier
Token appears expired

Fix JWT "Token appears expired"

The exp claim is earlier than now. What the claim means, why decoding still works, how clock skew produces false expiries, and what to do about it.

Input that triggers it

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNTE2MjM5MDIyfQ.dGVzdC1zaWduYXR1cmU
Open JWT Decoder on its own page
Sensitive tool. Nothing you enter here is saved or shared: no drafts, history, or links. Everything runs in this browser tab.

Local workspace

Named projects in IndexedDB · Local only — never synced to our servers. Worksp

Open manager
Inputtext
json

What the error means

A JWT payload can carry an exp claim: a Unix timestamp, in seconds, after which the token must not be accepted. The decoder compares it with the current time and shows Token appears expired when the claim is in the past. It is a warning about the token's validity, not about its structure: the header and payload decode normally and you can still read every claim.

The example token's payload is {"sub":"1234567890","exp":1516239022}. That timestamp is 18 January 2018, so the token has been expired for years. Paste it into Timestamp Converter to see the date; the decoder does the same conversion inline.

Why it happens

  • The token is genuinely old. Access tokens commonly live for minutes to an hour; anything copied from a log or a test fixture is likely past its exp.
  • Clock skew. If the machine that issued the token or the machine checking it has a clock that is off by more than a few seconds, a token can be reported expired the moment it is minted, or accepted after it should have died. Servers usually allow a small leeway (30–60 seconds) for this reason.
  • exp in milliseconds. The claim must be seconds. A producer that writes Date.now() without dividing by 1000 sets an expiry thousands of years away — the opposite bug — while one that mistakes seconds for milliseconds sets it in 1970.
  • A time zone mistake in the issuing code. exp is UTC-based by definition; local-time arithmetic shifts it by hours.

How to fix it

  1. Decode the token above and read exp and, if present, iat (issued at). The gap between them is the intended lifetime; iat in the far past means the token is simply old.
  2. If the token should be current, obtain a fresh one through the normal login or refresh flow. Expired tokens cannot be extended by editing; any verifying server checks the signature, and a changed payload fails it.
  3. If a freshly issued token is already "expired", compare the issuing server's clock with a reliable time source. Enable NTP; fix the skew rather than widening the leeway.
  4. If exp looks like a year in the 50000s or like 1970, the producer has a units bug — seconds, not milliseconds.

If it still fails

  • To test a client's expiry handling, JWT Generator can mint a token with any exp you choose, signed with a secret you supply and that is never stored.
  • nbf (not before) is the mirror claim: a token that is not yet valid. Check it if a new token is rejected without being expired.
  • The decoder runs in sensitive mode — nothing you paste is saved, shared, or kept in history — so a live production token is safe to inspect.

Related errors