JWT-related errors can be frustrating because the error messages are often vague. This guide covers the most common issues developers encounter, explains what's actually going wrong, and provides step-by-step fixes. For each error, you can use our JWT decoder to inspect the token and identify the problem.
Quick Diagnosis
Start by decoding your token to inspect the header and payload. Most issues become obvious once you can see the token's contents. Here are the first things to check:
- Does the token have exactly three parts separated by dots?
- Does the header contain the expected
algvalue? - Is the
expclaim in the future? - Do the
issandaudclaims match your expected values? - Are you using the correct key for the algorithm type?
Invalid Signature
Error messages: JsonWebTokenError: invalid signature, Signature verification failed,JWT signature does not match
What's Happening
The server computed the expected signature using the token's header and payload combined with the verification key, but the result doesn't match the signature in the token.
Common Causes
- Wrong secret key — The most common cause. The verification key doesn't match the key used to sign the token. Check that the key hasn't been rotated or that you're not mixing up keys between environments (dev vs production).
- Algorithm mismatch — The token was signed with RS256 but you're trying to verify with HS256 (or vice versa). Check the
algheader in the decoded token. - Token was modified — If any part of the token (header or payload) was changed after signing, the signature will be invalid. Even a single character change breaks the signature.
- Key encoding issues — For RSA/ECDSA, the public key might have incorrect PEM formatting (missing headers, wrong line breaks, extra whitespace). Ensure the key starts with
-----BEGIN PUBLIC KEY-----. - Base64 vs Base64URL — The secret might be Base64-encoded but the library expects a raw string, or vice versa.
How to Fix
- Decode the token and check the
algheader - Verify you're using the correct key for that algorithm
- For HMAC: ensure the secret is the exact same string/bytes used for signing
- For RSA/ECDSA: ensure you're using the public key (not private) for verification
- Check key encoding — try both raw and Base64-decoded versions of your secret
Token Expired
Error messages: TokenExpiredError: jwt expired, Token has expired,exp claim check failed
What's Happening
The token's exp (expiration) claim is a Unix timestamp that has already passed. The server correctly rejects the expired token.
Common Causes
- Token genuinely expired — The simplest case. Access tokens have short lifetimes (often 15–60 minutes). Use the refresh token to get a new access token.
- Clock skew — The server's clock is slightly behind the clock that issued the token. Even a few seconds of difference can cause tokens near their expiration time to be rejected.
- Timezone confusion — The
expvalue was set using local time instead of UTC, or was calculated in seconds but treated as milliseconds (or vice versa). - Missing refresh logic — The client isn't refreshing the token before it expires.
How to Fix
- Decode the token and check the
expvalue — convert the Unix timestamp to a readable date - If the token just expired, implement token refresh logic on the client
- For clock skew: configure a tolerance window in your JWT library (e.g., 30 seconds)
- Ensure
expis a Unix timestamp in seconds, not milliseconds - Always use UTC when setting expiration times
Malformed Token
Error messages: JsonWebTokenError: jwt malformed, Invalid token format,Unexpected token in JSON
What's Happening
The token doesn't conform to the JWT structure: three Base64URL-encoded segments separated by dots. The library can't parse the token before it even gets to signature verification.
Common Causes
- Including "Bearer " prefix — The
Authorizationheader value isBearer eyJ.... If you pass the full header value to the JWT library instead of just the token part, parsing fails. - Truncated token — The token was cut off during copy-paste, logging, or URL encoding. Check that the full token is being transmitted.
- URL encoding — The token was double-encoded or the dot separators were percent-encoded (
%2Einstead of.). - Not a JWT at all — The value is an opaque session token, API key, or other credential that the code is trying to parse as a JWT.
- Line breaks or whitespace — Extra whitespace or newlines were introduced when copying the token between systems.
How to Fix
- Strip the "Bearer " prefix before passing to your JWT library
- Trim whitespace and newlines from the token string
- Verify the token has exactly three dot-separated parts
- Try decoding each part individually with Base64URL to check for corruption
- Log the raw token value (in development only) to see exactly what's being received
Algorithm Mismatch
Error messages: Algorithm not allowed, invalid algorithm,Expected RS256, got HS256
What's Happening
The algorithm in the token's header doesn't match what the server is configured to accept. This can be a legitimate configuration issue or a potential attack.
Common Causes
- Configuration mismatch — The auth server signs with one algorithm, but the resource server is configured to expect a different one. Common when migrating algorithms or setting up a new service.
- Library defaults — Some libraries default to HS256. If your auth server uses RS256, the defaults won't match.
- Multiple issuers — Different auth servers in your system use different algorithms. The resource server needs to accept all of them (or use the
kidheader to look up the correct key and algorithm).
How to Fix
- Decode the token and check the
algvalue in the header - Match your server's algorithm configuration to the issuer's algorithm
- If using JWKS, ensure the
kidin the token matches a key in your key set - Always explicitly set the algorithm in your verification code — never let it default
Issuer or Audience Mismatch
Error messages: jwt issuer invalid, jwt audience invalid,iss claim does not match
What's Happening
The token's iss or aud claim doesn't match the value the server expects. The server is rejecting the token because it wasn't issued by a trusted authority or wasn't intended for this service.
Common Causes
- Environment mismatch — Token from staging being used against production (or vice versa). The issuer URLs differ between environments.
- Trailing slashes — The issuer is
https://auth.example.comin the token buthttps://auth.example.com/in the server config (or vice versa). These are different strings. - Wrong audience — In multi-service architectures, a token intended for Service A is being sent to Service B, which has a different audience value.
How to Fix
- Decode the token and check the exact
issandaudvalues - Compare character-by-character with your server configuration (watch for trailing slashes, different protocols)
- Ensure environment-specific URLs are correctly configured
- If
audis an array, verify your service's identifier appears in the list
Key Format Errors
Error messages: PEM routines: get_name: no start line, Invalid key format,error:0909006C:PEM routines
What's Happening
The public or private key can't be parsed. The cryptographic library doesn't recognize the key format.
Common Causes
- Missing PEM headers — PEM keys must include the
-----BEGIN PUBLIC KEY-----and-----END PUBLIC KEY-----markers. - Wrong key type — Using an RSA private key where a public key is expected, or using a PKCS#1 key where PKCS#8 is expected.
- Line break issues — PEM keys need line breaks every 64 characters. If the key was stored as a single line (common in environment variables), it needs to be reformatted.
- JWK format confusion — Passing a JWK (JSON object) to a function that expects PEM format, or vice versa.
How to Fix
- Ensure PEM keys have proper begin/end markers and line breaks
- For environment variables, replace
\nliteral strings with actual newlines - Use our decoder to paste your key and check if it works for verification
- Convert between formats using
openssl:openssl rsa -pubin -in key.pem -outform PEM
CORS Issues with JWT
Error messages: Access-Control-Allow-Origin errors, Authorization header not allowed, preflight request failures
What's Happening
The browser blocks the request because the server's CORS configuration doesn't allow theAuthorization header from the client's origin.
How to Fix
Configure your server's CORS middleware to:
- Allow the client's origin in
Access-Control-Allow-Origin - Include
AuthorizationinAccess-Control-Allow-Headers - Handle OPTIONS preflight requests (required for any request with custom headers)
- If using cookies, set
Access-Control-Allow-Credentials: true
// Example CORS headers Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Headers: Authorization, Content-Type Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Credentials: true
General Debugging Tips
- Always decode first — Before digging into server logs, paste the token into a decoder to see what's actually in it
- Check timestamps carefully — Convert Unix timestamps to human-readable dates. Remember JWT uses seconds, not milliseconds
- Compare environments — Many issues come from mismatched configuration between dev, staging, and production
- Test with a known-good token — Generate a fresh token with your server and verify it works. This isolates whether the issue is with token creation or verification
- Check your library version — JWT library bugs and security patches are common. Ensure you're on a recent version
- Log the raw Authorization header — In development, log the exact header value to catch truncation, extra whitespace, or encoding issues
Decode & Debug Your Tokens
Paste your JWT token to instantly see the header, payload, and verify the signature.