Join our Newsletter — 33% off our NHI Course
Home› Guides› JWT Validation Errors Troubleshooting Guide
Guide Authentication

JWT Validation Errors Troubleshooting Guide

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 12 min read
On this page

A JWT validation error is the verifier doing its job, and the most dangerous fix is the one that makes the error go away by validating less. When an API starts returning 401s, engineers are tempted to widen the audience list, add clock tolerance, accept another algorithm or skip signature checks "temporarily". This guide maps the real error messages from three widely used libraries (Microsoft.IdentityModel for .NET, jsonwebtoken for Node.js and PyJWT for Python) to the validation stage that failed, lists the likely causes from most to least common, and gives the fix that keeps the check intact. It is for API, platform and identity engineers running OAuth 2.0 or OpenID Connect resource servers. Library messages were checked against each project's source or documentation on 11 November 2026. JWT design and hardening are covered in the JWT Security Guide, which this page does not repeat.

Key takeaways

  • Locate the failing stage first. Every JWT error belongs to one stage: parsing, algorithm, key lookup, signature, issuer, audience, time window or required claims. The message usually names it.
  • Signature failures after a quiet period are usually key rotation. A verifier with a pinned key or a stale JWKS cache rejects tokens signed with the issuer's new key; refresh on unknown kid, rate limited.
  • Issuer and audience mismatches are configuration, not attacks, most of the time. Microsoft v1.0 and v2.0 tokens carry different issuers, and an API must accept only tokens whose aud is its own identifier.
  • Library defaults differ on clock skew. Microsoft.IdentityModel allows 300 seconds by default; PyJWT's leeway defaults to 0. The same token can pass one service and fail the next.
  • Never fix by validating less. Do not decode without verifying, accept every algorithm in the header, or add another resource's audience. Storm-0558 showed what happens when a verifier trusts a key it should not.

Find the failing stage: message to stage map

Verifiers check a token in a broadly fixed order. Match the message to the stage, then go to that section.

StageMicrosoft.IdentityModel (.NET)jsonwebtoken (Node.js)PyJWT (Python)
ParsingIDX10209 token larger than MaximumTokenSizeInBytesjwt malformed; invalid tokenDecodeError
Signature presentIDX10504 token does not have a signaturejwt signature is requiredDecodeError
AlgorithmSurfaces as a signature failureinvalid algorithm when the token's alg is not in the allowed listInvalidAlgorithmError
Key lookupIDX10500 no security keys provided; IDX10503 / IDX10517 (kid missing)Your getKey callback errorPyJWKClientError; PyJWKClientConnectionError
SignatureIDX10511 signature validation failed, keys triedinvalid signatureInvalidSignatureError
IssuerIDX10205 issuer validation failed; IDX10204 no valid issuer configuredjwt issuer invalid. expected: ...InvalidIssuerError
AudienceIDX10214 / IDX10215 audience validation failed; IDX10208 none configuredjwt audience invalid. expected: ...InvalidAudienceError
ExpiryIDX10223 the token is expiredTokenExpiredError: jwt expiredExpiredSignatureError
Not beforeIDX10222 the token is not yet validNotBeforeError: jwt not activeImmatureSignatureError
Required claimsIDX10225 missing an Expiration TimeSubject or jwtid mismatch messagesMissingRequiredClaimError

Microsoft.IdentityModel hides token contents in messages by default (PII logging off). Turning PII logging on in a test environment shows the actual issuer and audience values compared; do not enable it in production, because messages then include token data.

Inspect the token safely

  1. Capture from a non-production request or from the failing client's debug output. Treat it as a live credential until exp passes; never paste production tokens into online decoders. See the JWT and Session Tokens in Code and Logs Guide.
  2. Check the shape: a JWS has three base64url segments separated by dots. Two dots and an empty third segment means unsigned; five segments means it is encrypted (JWE); no dots means it is an opaque token that must be introspected, not validated as a JWT.
  3. Read the header: alg, kid and typ. RFC 9068 access tokens carry typ at+jwt.
  4. Read the payload: iss, aud, exp, nbf, iat, tid or tenant claims, scp or roles.
  5. Compare with the verifier's configuration character for character, then with the issuer's discovery document (/.well-known/openid-configuration) and its jwks_uri.

Signature and key errors

Likely causes, most to least common

  1. Issuer rotated its signing key and the verifier has not caught up. The token's kid is not in the cached key set. Microsoft states that Entra ID rotates keys periodically, applications must handle changes automatically, and checking for updated keys every 24 hours is reasonable.
  2. Pinned key or certificate in configuration. A public key copied into app settings works until the first rotation, then fails everywhere at once.
  3. Wrong metadata endpoint. Tenant-specific, common and v1.0 or v2.0 metadata paths publish different documents; a verifier pointed at the wrong one fails on issuer or key.
  4. Token is not meant for this API. Microsoft says Microsoft Graph tokens cannot be validated with the standard rules because of their proprietary format, and accepting tokens meant for another resource is a confused deputy problem. A client that sends its Graph token to your API produces a signature error.
  5. Algorithm mismatch. A verifier expecting RS256 receives HS256 (a test token signed with a shared secret) or ES256 (a new issuer). Node's jsonwebtoken reports this as invalid algorithm when the header alg is not in its allowed list.
  6. Token altered in transit: a proxy, header size limit or logging middleware truncates the Authorization header.

Diagnose

  • Is the kid in the live JWKS? Fetch the issuer's jwks_uri and search for it. If yes, the verifier's cache is stale. If no, the token came from a different issuer or tenant, or is forged.
  • What keys did the verifier try? IDX10511 lists keys tried, the number of keys in TokenValidationParameters and in configuration, and where the matched key was found. Zero keys in configuration means metadata was never retrieved; check outbound network access to the issuer.
  • PyJWKClient: PyJWKClientConnectionError means the JWKS request failed; PyJWKClientError means no matching signing key even after a refresh.

Fix

  • Load keys from metadata, never from config. Microsoft.IdentityModel's ConfigurationManager refreshes metadata automatically every 12 hours by default and allows a forced refresh no more often than every 5 minutes by default. PyJWKClient caches the JWK set for 300 seconds by default and applies a 30-second cooldown between forced refreshes after an unknown kid.
  • Refresh on unknown kid, rate limited: one refresh, then reject. Unlimited refetching lets an attacker turn random kid values into a denial of service against the issuer and you.
  • Pin the algorithm list in code: PyJWT's documentation tells you to hard-code algorithms rather than derive it from the token. Use the algorithm the issuer publishes.
  • Return the token to sender: if the client sent a token for another resource, the fix is in the client's scope request, not your verifier.

Key rollover from the issuer's side is covered in the JSON Web Key Sets (JWKS) and Signing Key Rotation Guide.

Issuer errors

  • v1.0 versus v2.0 Microsoft tokens: the v2.0 issuer is https://login.microsoftonline.com/{tenantid}/v2.0; v1.0 tokens carry the https://sts.windows.net/{tenant}/ form. The API's manifest property requestedAccessTokenVersion decides which the API receives (null or 1 gives v1.0, 2 gives v2.0). A mismatch between that setting and the configured issuer is the most common IDX10205 on Entra-protected APIs.
  • Trailing slash and scheme: issuer comparison is an exact string match. https://idp.example.com and https://idp.example.com/ are different issuers.
  • Multi-tenant APIs: the metadata issuer contains a {tenantid} placeholder. Microsoft's guidance is to substitute the token's tid, confirm tid is a GUID, then compare with iss, and separately decide which tenants you accept. Accepting any issuer that matches the pattern accepts every tenant in the cloud.
  • Environment drift: staging tokens presented to production, or a custom domain added to the IdP that changes iss.
  • Nothing configured: IDX10204 means neither ValidIssuer nor ValidIssuers is set. Configure it; do not switch issuer validation off.

Audience errors

  • Client ID versus App ID URI: Entra tokens may carry the API's application (client) ID or its App ID URI in aud, depending on token version and how the client requested scope. Configure the values your API actually receives, both if needed, and nothing else. Microsoft: APIs must only accept tokens containing one of their App ID URIs as the aud claim.
  • Client requested the wrong resource: the token's aud is Graph or another API. Fix the client's scope parameter (for example api://your-api/.default in client credentials).
  • Array audiences: RFC 7519 allows aud as a string or array. Most libraries pass if any value matches; check your library does not require the first element.
  • Never add another resource's identifier to ValidAudiences to make an error disappear. That is the confused deputy Microsoft warns against.

Time window errors: expired, not yet valid, skew

LibraryClock skew defaultSetting
Microsoft.IdentityModel300 secondsTokenValidationParameters.ClockSkew
PyJWT0 secondsleeway on decode()
jsonwebtoken (Node.js)Not set unless configuredclockTolerance (seconds) on verify()
  1. Client caches tokens past expiry: the most common cause of jwt expired. The client must refresh before exp, not after a 401. Entra assigns access tokens a random lifetime of 60 to 90 minutes (75 on average) by default, so a client hard-coding "one hour" will sometimes send expired tokens.
  2. Clock drift between hosts: IDX10222, jwt not active or ImmatureSignatureError on freshly issued tokens means the verifier's clock is behind the issuer's. Fix NTP on the host or container node; a leeway of 30 to 60 seconds is a reasonable buffer, minutes is not.
  3. Long-running jobs: a batch job acquires one token and uses it for hours. Acquire per call or refresh on a timer.
  4. Missing exp: IDX10225. A token without expiry should be rejected; fix the issuer, not the verifier.

Malformed tokens and missing claims

  • Header formatting: Bearer prefix sent twice, missing, or the token wrapped in quotes from a JSON config. Strip exactly one Bearer prefix.
  • Opaque or encrypted token: some IdPs issue opaque access tokens unless an audience or API is specified in the request; others encrypt. Validate the format you receive: introspect opaque tokens (RFC 7662), decrypt JWEs with the right key.
  • Size: Microsoft.IdentityModel rejects tokens above 250 KB by default (IDX10209); group overage claims and large role lists push tokens towards proxy header limits long before that.
  • Required claims: PyJWT's options.require checks presence only, not validity, so pair it with the issuer and audience arguments.

Returning errors correctly to clients

Clients can only self-correct if the API responds per RFC 6750 section 3.1.

Error codeWhenHTTP status (RFC 6750)Client action
(none)No credentials sent401 with WWW-Authenticate: BearerAcquire a token
invalid_requestMalformed request, token sent by more than one method400Fix the request
invalid_tokenExpired, revoked, malformed or otherwise invalid401May get a new token and retry
insufficient_scopeValid token lacking required privileges403, optionally with scopeRequest the scope, do not retry blindly

Keep error_description generic in production. Log the detailed reason (stage, kid, iss, aud, client ID) server-side.

Tempting fixes and the safe alternative

Tempting fixWhy it is dangerousSafe fix
Use decode() instead of verify()jsonwebtoken warns decode does not verify the signature; any forged token passesFix key retrieval
Accept the algorithm named in the token headerOpens algorithm confusion attacks (RFC 8725)Allowlist the issuer's algorithm in code
Turn off issuer or audience validationAccepts tokens from any tenant or meant for any APIConfigure the exact values
Set clock skew to hoursExtends the life of stolen tokensFix NTP; keep leeway under a minute or two
Hard-code the current public keyBreaks at next rotation and blocks emergency rolloverLoad from jwks_uri with caching

Detection and measurement

  • Log per failure: stage, library error code, kid, iss, aud, client ID and source. Never log the token.
  • Alert on: signature failures with a kid absent from every trusted JWKS (possible forgery), sudden spikes in one stage after a deployment or IdP change, and tokens from unexpected tenants.
  • Metrics: 401 rate by validation stage; share of signature failures caused by unknown kid; time from issuer key rotation to zero kid-not-found errors; count of verifiers with pinned keys or disabled checks (target zero); median token remaining lifetime at use.

Common failure patterns

  • Trusting a key outside its scope: in the Microsoft Storm-0558 key breach, tokens signed with a consumer signing key were accepted for enterprise mailboxes. Check issuer and key scope together.
  • Fail-open on JWKS outage: code that skips signature checks when the key fetch fails. Fail closed and use the last known good key set.
  • Global leeway raised during an incident and never lowered.
  • Accepting ID tokens as access tokens: check typ and token purpose (OWASP ASVS V9.2.2).
  • Every service validating differently: centralise validation in a gateway or shared library so one fix applies everywhere.

Framework mapping

FrameworkReferenceHow it applies
OWASP ASVS 5.0V9.1.1, V9.1.2, V9.2.1, V9.2.2, V9.2.3Signature validation, algorithm allowlist, validity window, token type, audience
NIST SP 800-53 Rev 5SC-23 Session Authenticity; IA-9 Service Identification and Authentication; SC-12Accept only authentic tokens; authenticate services; key management for signing keys
NIST CSF 2.0PR.AA-03Users, services and hardware are authenticated
MITRE ATT&CK v20T1606 Forge Web Credentials; T1550.001 Application Access TokenForged tokens and stolen tokens replayed against APIs

Practitioner checklist

  • Every failure logged with stage and library code, never the token.
  • Keys loaded from the issuer's metadata, with refresh on unknown kid rate limited.
  • Algorithm allowlist hard-coded per issuer.
  • Issuer and audience set to exact values; multi-tenant APIs restrict accepted tenants.
  • Clock skew under two minutes, and NTP monitored on verifier hosts.
  • Clients refresh tokens before exp.
  • Responses follow RFC 6750 error codes.
  • No production code path calls a decode-only function on untrusted tokens.

Standards and references

Related NHI Mgmt Group resources: JWT Security Guide, JSON Web Key Sets (JWKS) and Signing Key Rotation Guide, OAuth 2.0 and OpenID Connect Guide, Machine-to-Machine OAuth (Client Credentials) Guide, Secret Rotation Outage Troubleshooting Guide.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org