JWT "jwt expired" (TokenExpiredError) in CI
The JWT verified structurally and by signature, but its exp claim is in the past. jsonwebtoken raises "TokenExpiredError: jwt expired" with the expiration timestamp.
What this error means
Verification throws "TokenExpiredError: jwt expired" with an expiredAt value. A hardcoded test token or a token minted before a slow test finished has aged out.
TokenExpiredError: jwt expired
at /app/node_modules/jsonwebtoken/verify.js:190:21
expiredAt: 2026-06-29T10:00:00.000ZCommon causes
A stale hardcoded fixture token
A token committed to the repo has a fixed exp in the past, so it is expired every run.
Runner clock skew or a slow test
A short-lived token expires before verification if the job is slow or the runner clock differs from the issuer.
How to fix it
Mint tokens at test time
- Generate the JWT inside the test with a future exp instead of committing one.
- Use a generous expiresIn for fixtures so slow jobs do not age it out.
- Verify the runner clock is roughly correct.
const token = jwt.sign({ sub: 'ci' }, secret, { expiresIn: '1h' });Allow small clock skew when appropriate
For provider tokens, set a small clockTolerance so minor runner clock drift does not reject a still-valid token.
jwt.verify(token, key, { clockTolerance: 30 });How to prevent it
- Generate fixture tokens at runtime with a future exp.
- Keep runner clocks synchronized with the issuer.
- Set a small clockTolerance for provider-issued tokens.