GitHub App Installation Token Tutorial: Fix the New 520-Character Format
GitHub now issues stateless installation tokens of about 520 characters. Learn how to test, store, validate, and safely migrate GitHub App integrations.
On this page
GitHub has finished rolling out a new format for GitHub App installation tokens, and the change can break integrations that assumed every token was exactly 40 characters long. Newly minted installation tokens now use the stateless ghs_APPID_JWT format and are about 520 characters instead of the old short opaque value. This tutorial shows how to test your integration, find the assumptions that can fail, and update your code and storage safely before GitHub removes the temporary compatibility switch on November 30, 2026.
Why the GitHub App token format changed
A GitHub App uses an installation access token to authenticate API requests as a particular installation of the app. The token is temporary, scoped to the installation's permissions, and normally expires after one hour. GitHub began a staged rollout of a stateless format on April 27, 2026, and completed that rollout on October 2, 2026. GitHub says the new format improves token issuance performance and API reliability, while the token's permissions, repository scope, lifetime, and API endpoint remain unchanged.
The practical difference is the token's representation. Older installation tokens were short opaque strings, while the new tokens still begin with ghs_ but use a JSON Web Token (JWT)-style structure represented as ghs_APPID_JWT. GitHub says the new values are about 520 characters long and can vary in length, so code that only checks for the old size can reject a perfectly valid token. The security model does not change simply because the string became longer; the problem is compatibility with software that incorrectly treated the token's shape as a fixed specification.
Check whether your integration assumes 40 characters
Start by searching the application that receives or stores GitHub App installation tokens. Look for code that slices a token to a fixed length, validates it with an exact-length regular expression, allocates a database field sized for the old value, or rejects characters that were not present in the classic token. Also inspect middleware, API gateways, logging filters, secret scanners, and configuration validators because the failure may occur outside the code that actually calls GitHub.
GitHub specifically recommends checking for validation that requires exactly 40 characters, database columns or secret stores with small maximum lengths, proxies that truncate long authorization headers, and redaction rules that only recognize the old token pattern. These are not hypothetical compatibility concerns: GitHub's own rollout guidance identifies them as the areas developers should audit.
ghs_123456789012345678901234567890123456A check such as token.length === 40 is therefore the wrong abstraction. The application needs to treat the token as an opaque credential whose contents and length it does not need to interpret. If you have a legitimate reason to validate its general shape, the validation must accommodate both the classic and stateless forms rather than encoding the old format as a permanent rule.
Force the new token format during testing
GitHub introduced a temporary request header specifically for testing the transition. When creating an installation access token, send X-GitHub-Stateless-S2S-Token: enabled to force the new stateless format for that request. This lets you test your application even if its normal rollout path has not yet exposed the same behavior. GitHub's documentation says the header applies to the installation access-token endpoint and can be used to validate an application before relying on the normal rollout.
curl -X POST \ -H "Authorization: Bearer APP_JWT" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Stateless-S2S-Token: enabled" \ "https://api.github.com/app/installations/INSTALLATION_ID/access_tokens"Replace APP_JWT with the JSON Web Token used to authenticate your GitHub App and INSTALLATION_ID with the installation you want to authenticate. The response contains the installation token in its token field. You should not log the returned credential itself; inspect its length or structure in a controlled test instead. GitHub's endpoint documentation confirms that the installation token is created from an app-authenticated request and expires one hour after creation.
Update token validation to accept the new format
The safest fix is to stop validating an installation token by exact length. Your application generally does not need to decode the credential or inspect its internal JWT claims before sending it to GitHub. It needs to store the value safely and send it as a bearer token. Treating the credential as an opaque string also means a future change in representation is less likely to require another code modification.
If your application needs a basic format check before accepting an installation token, GitHub's rollout guidance gives a pattern that accommodates both the classic and stateless forms:
ghs_[A-Za-z0-9\.\-_]{36,}The key difference is the absence of a fixed 40-character requirement and the allowance for characters used by the new representation. GitHub notes that stateless tokens contain two dots because they use a JWT-style structure, while classic opaque tokens do not. That distinction can help during diagnostics, but it should not become a reason to decode or trust the token locally; GitHub remains responsible for validating the credential when it is used.
Increase database and secret-storage capacity
If your application stores installation tokens, inspect the actual database column and not just the application model. A field designed around the old token can silently truncate a new value, reject an insert, or create an authentication failure later when the stored credential is reused. GitHub says new tokens are approximately 520 characters and their length can vary, so a column sized narrowly around that figure is still a brittle design.
The better design is to use a text field or secret-storage mechanism that comfortably accepts the full credential without imposing an arbitrary token-length rule. The same principle applies to environment variables, configuration services, encrypted credential stores, and API gateways. Do not use the token's current maximum as a magic number when there is no technical reason to limit it.
After changing the storage layer, test the complete path rather than stopping at the database. Generate a new stateless token, store it, retrieve it, place it in the authorization header, and make a harmless authenticated GitHub API request. This catches truncation and encoding problems that unit tests against a short fixture may never reveal.
Check regular expressions and secret scanners
Regular expressions are another common failure point because older tooling may have encoded the assumption that an installation token is a short alphanumeric string. A pattern such as ghs_[A-Za-z0-9]{36} will not match the new representation correctly. GitHub's own migration guidance calls out regex validation as something developers should review and provides a broader pattern for recognizing the new and old forms.
Search your repository for ghs_, 36, 40, and any token-validation helper used by GitHub integrations. Then check secret scanners and log-redaction rules separately. A scanner that fails to recognize the new format could expose credentials in logs, while a redaction rule that assumes the old length could leave part of a token visible. The safest logging rule is still simple: never print installation access tokens in the first place.
Test an API call with the resulting installation token
After fixing validation and storage, verify that the token still works as an authentication credential. GitHub's REST API uses the installation token as a bearer credential, and the change to stateless tokens does not alter the installation's permissions or repository scope. A successful API call therefore proves more than merely receiving a longer string; it verifies that the complete token-handling path is compatible.
curl \ -H "Authorization: Bearer INSTALLATION_TOKEN" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "https://api.github.com/installation/repositories"Do not paste a real token directly into shell history on a shared machine. For a controlled test, place the credential in an environment variable and expand it at execution time, or use the authentication mechanism already provided by your application. The expected result is an authenticated response containing repositories available to that installation; an authentication failure means you should inspect token storage, truncation, headers, and permissions in that order.
Remove the temporary compatibility header before November 30
The temporary X-GitHub-Stateless-S2S-Token header is a migration aid, not a permanent configuration option. GitHub says it will stop respecting the header on November 30, 2026, after which eligible apps will always receive stateless installation tokens. Once your integration accepts both formats, remove the header from production code rather than leaving an obsolete compatibility switch in place.
During testing, you can also use disabled to request the classic format for a single token-creation request. That is useful for proving that your application handles both representations, but the production target should be format-independent code. GitHub's migration guidance explicitly recommends testing both paths and then removing the override once the application has been validated.
Use a token-handling design that survives future format changes
The real fix is broader than changing a regular expression. GitHub's installation token is a credential issued by an external service, so application code should depend on its documented behaviorβauthentication, permissions, expiration, and scopeβrather than incidental properties such as length or character count. The new rollout is a useful test of that boundary because the old 40-character assumption was never necessary for making an authenticated API request.
Once the new format works end to end, keep the installation token opaque, store it without arbitrary length restrictions, avoid logging it, and let GitHub determine whether it is valid. Installation tokens still expire after one hour, so applications should continue requesting fresh tokens when necessary rather than trying to preserve them indefinitely.
The immediate deadline is November 30, but the useful engineering lesson lasts longer: credentials should be validated according to their contract, not according to what one generation of their string representation happened to look like. If your GitHub App passes that test now, the next token-format change becomes a compatibility check instead of an outage.
Written by


