The ‘JSON Web Token could not be decoded’ error usually means the token format, signing key, or timestamps do not match what the server expects.
Seeing a bare JSON Web Token could not be decoded message from GitHub, an API gateway, or your own backend feels vague.
You know the token failed somewhere, yet the server gives almost no detail.
The good news is that this error nearly always comes from a short list of repeat problems that you can track down in a structured way.
This guide walks through what the message actually means, why a JSON Web Token could not be decoded in common setups,
and the checks that usually uncover the root cause.
You will see fixes for GitHub App authentication, Node.js and other libraries, plus a few habits that keep the same issue from returning later.
What ‘JSON Web Token Could Not Be Decoded’ Actually Means
A JSON Web Token (JWT) is a compact string with three base64url-encoded parts separated by dots: header, payload, and signature.
When a server receives a token, it first splits the string, decodes the header and payload from base64url, then verifies the signature and claims.
The message JSON Web Token Could Not Be Decoded usually appears when the server cannot even complete that first decode step.
The token might be missing sections, corrupted, or encoded with an unexpected key or algorithm.
In many platforms this turns into a generic HTTP 401 response with that short error text attached.
Another detail matters here: decode is not the same as verify.
Decode problems happen before any checks on the issuer, audience, or signature.
If you fix the raw token shape and base64 issues, you might then run into clearer messages about expiration or an invalid signature, which are separate stages.
In hosted systems such as GitHub Apps, API gateways, and identity providers, the error often comes from a mismatch between how you created the token and how the platform expects it.
That mismatch can involve the private key format, the app or client identifier, or simple issues such as a wrong environment variable.
Json Web Token Error Could Not Be Decoded In Common Setups
Before diving into tools, it helps to see the typical patterns that cause a JSON Web Token could not be decoded message.
Most real incidents fall into a small set of causes that repeat across languages and vendors.
| Cause | Typical Symptom | Where To Look First |
|---|---|---|
| Malformed token string | Less than three segments, stray quotes, line breaks | Client code, HTTP headers, copy-paste or logging output |
| Wrong key or key format | Token always rejected across all requests | Private key file, secret storage, GitHub App or OAuth config |
| Bad timestamps or clock drift | Works on one machine, fails on another | System time, iat and exp fields in the payload |
| Mismatched algorithm or kid | Decode failure when rotating keys or changing algorithms | JWT header (alg, kid), server configuration, JWKS |
| Not passing the token at all | Library throws decode error for null or empty string |
Authorization header, cookies, client request builder |
When an API returns only that one line, it can feel like anything could be wrong.
In practice, checking these spots in order removes a lot of guesswork and keeps you from changing code that already works.
Check The Basics Before You Change Any Code
A big share of decode errors come from simple shape problems in the token that reaches the server.
Quick checks on the raw value often fix the issue long before you open a debugger.
- Confirm the token string is present — Log the exact variable you pass into your JWT decode or verify call.
Many libraries throw a decode error when the value isnull,undefined, or an empty string. - Check for stray quotes or prefixes — In some clients the token ends up wrapped in quotation marks, or only the word
Beareris sent.
Make sure the Authorization header looks likeBearer. - Look for line breaks or truncation — Long tokens copied from logs or consoles can lose characters at the end.
When pasting into tools, confirm that you still see the full three segments separated by dots. - Try a local decoder with safe data — For non-production tokens, use a trusted decoder such as
jwt.ioor your library’s decode method without verification.
If those tools also fail, the token itself is malformed. - Verify the same token in a second environment — When a token decodes on your laptop but not in CI or on the server, that points you toward environment variables, key files, or time settings.
If these quick checks show that the token has the right three segments and decodes locally, the next step is to line up keys, algorithms, and timestamps with the rules from your platform or library.
Fixes For GitHub App And Similar Integrations
GitHub’s REST and GraphQL APIs often show this exact text when authentication with a GitHub App fails.
In that setting, the error usually means the app JWT cannot be decoded on GitHub’s side, not that your installation token or user token is wrong.
Match The App Id And Private Key
The JWT for a GitHub App is created with a private key that belongs to that app and an iss value equal to the app’s identifier.
If you paste in a key from a different app, or use the wrong identifier, GitHub cannot decode the token and responds with the familiar message.
- Check the app identifier — Compare the
issclaim in your payload with the numeric App ID shown in the GitHub App settings page, not the client ID. - Re-download the private key — Generate a fresh key from the GitHub App settings, store it as a secret, and remove older keys so you know exactly which one the app uses.
- Keep the header and footer lines — When storing the private key in a secret, include the
-----BEGIN PRIVATE KEY-----and-----END PRIVATE KEY-----lines and the internal line breaks.
Use The Expected Key Format
Some actions and libraries that work with GitHub Apps expect the key in PKCS#8 format, which starts with BEGIN PRIVATE KEY,
while older downloads can come in PKCS#1 format with BEGIN RSA PRIVATE KEY.
When those differ, GitHub cannot decode the JSON Web Token and raises the same generic message.
- Check the key header — If your key starts with
BEGIN RSA PRIVATE KEY, convert it to PKCS#8 using OpenSSL or the script suggested in your action’s documentation. - Store the converted key as a secret — Paste the new key into your CI secret, confirm that line breaks remain, and trigger the workflow again.
Fix Time Claims And Clock Drift
GitHub requires the app JWT to have an iat (issued-at) time close to the current time and an exp no more than ten minutes in the future.
If the machine clock drifts or the claims fall outside that window, the server may fail the token, sometimes with a decode style error instead of a precise message.
- Sync system clocks — In CI and on servers, enable NTP or the hosted runner’s time sync so that the timestamp in the payload matches GitHub’s expectation.
- Rebuild the payload each request — Create the JWT fresh for each call instead of reusing a cached token that might have expired.
After these fixes, many GitHub App setups move from “token could not be decoded” to a clean 201 or 200 response, or to a more specific error that points to installation permissions instead of token structure.
Fixes For Custom APIs And Library Errors
In your own services you might not see the phrase JSON Web Token Could Not Be Decoded word for word,
yet libraries such as jsonwebtoken in Node.js, JWT handling in .NET, or similar tools return close messages like jwt malformed or “payload is not a valid JSON object”.
The root causes are similar.
Match Algorithms And Secrets
A token signed with HS256 uses a shared secret string.
A token signed with RS256 uses a private key, while verification uses the related public key.
Passing an HS256 token into a verifier that expects RS256, or the other way around, often leads to errors that look like decode problems.
- Check the
algfield in the header — Confirm that your verification code lists the same algorithm and does not restrict the value incorrectly. - Use the right type of key — For HS256 pass the exact secret string, not a certificate file.
For RS256 pass the PEM key with header and footer, not a shortened copy.
Keep The Payload As A Valid Json Object
JWT payloads must be JSON objects at the top level.
If your signing code passes a raw string, an array, or already encoded JSON, many libraries create tokens that decoders flag as invalid.
- Sign a plain object — Pass an object such as
{ userId: 123 }into your sign call, notJSON.stringifyoutput. - Check for double encoding — When your payload already contains base64 or JSON text, treat it as a normal string property inside the object instead of re-encoding the whole token.
Handle Null Or Empty Token Inputs
Many decode problems in logs come from routes where the token was never sent.
Middleware that assumes a header exists can end up feeding undefined into the decode function, which then reports a malformed token.
- Guard your middleware — Before calling
jwt.verifyor similar functions, check that the Authorization header exists and that it starts withBearer. - Return a clear 401 message — When no token is present, send a response that states that the header is missing instead of passing the problem deeper into the stack.
Prevent Json Web Token Decode Errors In Future Deployments
Once you have fixed the immediate JSON Web Token Could Not Be Decoded issue, a few habits help keep similar problems from returning during releases or infrastructure changes.
Log Safely Around Tokens
Raw tokens should not appear in production logs, yet you can still record enough context to debug decode errors.
Log the presence of the header, the length of the string, the algorithm in the header, and which key identifier the server tried to use.
That context makes the next failure much easier to track down without exposing full secrets.
Treat Keys And Configuration As A Single Unit
Keys, algorithms, and issuer identifiers work as a set.
When you rotate one piece in that set, try to update and test the others at the same time.
Store the current algorithm and key version in configuration next to the key itself, and propagate that bundle as one change to each environment.
Keep Time And Dependencies In Good Shape
JWT handling is sensitive to time windows and library behavior.
A box with a drifting clock or an outdated token library often behaves strangely before it fails loudly.
- Monitor clock drift — Use system tools or cloud monitoring to alert when server time drifts more than a small margin away from a trusted source.
- Update JWT libraries regularly — Security fixes and small breaking changes can affect how errors show up, so test token creation and verification flows when you bump versions.
With these safeguards in place, the next time a service reports that a JSON Web Token could not be decoded, you will have a short checklist, cleaner logs, and predictable steps to confirm the token shape, keys, and environment.
That short path to a clear cause is exactly what you want when authentication stands between users and the features they need.
