JWT Troubleshooting Guide

Common JWT errors, what causes them, and how to fix them

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:

  1. Does the token have exactly three parts separated by dots?
  2. Does the header contain the expected alg value?
  3. Is the exp claim in the future?
  4. Do the iss and aud claims match your expected values?
  5. 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 alg header 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

  1. Decode the token and check the alg header
  2. Verify you're using the correct key for that algorithm
  3. For HMAC: ensure the secret is the exact same string/bytes used for signing
  4. For RSA/ECDSA: ensure you're using the public key (not private) for verification
  5. 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 exp value 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

  1. Decode the token and check the exp value — convert the Unix timestamp to a readable date
  2. If the token just expired, implement token refresh logic on the client
  3. For clock skew: configure a tolerance window in your JWT library (e.g., 30 seconds)
  4. Ensure exp is a Unix timestamp in seconds, not milliseconds
  5. 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 Authorization header 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 (%2E instead 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

  1. Strip the "Bearer " prefix before passing to your JWT library
  2. Trim whitespace and newlines from the token string
  3. Verify the token has exactly three dot-separated parts
  4. Try decoding each part individually with Base64URL to check for corruption
  5. 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 kid header to look up the correct key and algorithm).

How to Fix

  1. Decode the token and check the alg value in the header
  2. Match your server's algorithm configuration to the issuer's algorithm
  3. If using JWKS, ensure the kid in the token matches a key in your key set
  4. 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.com in the token but https://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

  1. Decode the token and check the exact iss and aud values
  2. Compare character-by-character with your server configuration (watch for trailing slashes, different protocols)
  3. Ensure environment-specific URLs are correctly configured
  4. If aud is 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

  1. Ensure PEM keys have proper begin/end markers and line breaks
  2. For environment variables, replace \n literal strings with actual newlines
  3. Use our decoder to paste your key and check if it works for verification
  4. 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 Authorization in Access-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.