Clerk error
Clerk: Refreshing the session token resulted in an infinite redirect loop
"Clerk: Refreshing the session token resulted in an infinite redirect loop" is a line @clerk/backend writes to the server log after a request went through Clerk's handshake redirect three times in a row. No error page follows: the request carries on as signed out, so you see your sign-in page right after switching to production keys. The message says this usually means the Clerk instance keys do not match. Copy the publishable key and the secret key from the same instance in the Clerk Dashboard, set both in Vercel for that environment and redeploy. A satellite-domain variant of the text points at a host the production instance does not know, such as a preview deployment. Reproduced with @clerk/backend 3.26.5 on 2026-10-31.
What the SDK does on the third redirect
In packages/backend/src/tokens/handshake.ts, checkAndTrackRedirectLoop counts the redirects in a cookie that expires after two seconds. On the fourth request it stops redirecting, prints the message with console.log and returns a signed-out result. In a local run with a made-up pk_live key and a __client_uat cookie but no session cookie, the first three requests were redirected and the fourth was not. The output below is shortened:
count 0 -> handshake client-uat-but-no-session-token https://clerk.example.com/v1/client/handshake?redirect_url=...
count 1 -> handshake (same redirect, cookie __clerk_redirect_count=2)
count 2 -> handshake (same redirect, cookie __clerk_redirect_count=3)
Clerk: Refreshing the session token resulted in an infinite redirect loop. This usually means that your Clerk instance keys do not match - make sure to copy the correct publishable and secret keys from the Clerk dashboard.
count 3 -> signed-out client-uat-but-no-session-tokenWhich host does the handshake redirect to?
The redirect goes to the Frontend API host that is encoded in the publishable key. Decode it and compare it with the production domain on the Domains page of the Clerk Dashboard, where Clerk lists the DNS records the instance needs; the production guide says DNS records can take up to 48 hours to propagate. A key from a different instance, or from the development instance, decodes to a different host.
node -e "console.log(Buffer.from(process.argv[1].split('_')[2], 'base64').toString())" pk_live_YOUR_PUBLISHABLE_KEYThe satellite message and preview deployments
The second text is chosen when the redirect reason is SatelliteCookieNeedsSyncing. It tells you to check that the request uses a configured primary or satellite domain for the production instance, and to use a development or staging Clerk instance for preview deployments. Clerk's environments guide says it is currently not possible to use production keys with a host's own preview domain, so a *.vercel.app preview needs development keys.
I changed keys while developing locally
For a development instance the SDK has its own text: "Handshake token verification failed due to an invalid signature. If you have switched Clerk keys locally, clear your cookies and try again." Old cookies from the other instance are the usual leftover after swapping keys.
Checklist
How to fix it, then check the fix
- 01Open the Clerk Dashboard for the production instance and copy the publishable key (pk_live_) and the secret key (sk_live_) from that one instance. Do not mix a key from the development instance with one from production.
- 02Set both in Vercel for the Production environment, then redeploy. Vercel applies changed variables only to new deployments:
vercel env add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY production vercel env add CLERK_SECRET_KEY production - 03Decode the publishable key with the command above and check that its host belongs to the production domain whose DNS records the Dashboard shows as verified.
- 04On Preview and Development, set the development keys (pk_test_ and sk_test_) so the preview host is never sent to the production instance.
- 05Open the site in a new private window, sign in, and open the Vercel function log for the request: the redirect loop line is gone and the page loads signed in.
Related
Related guides
Sources
Official docs behind this checklist
- @clerk/backend, tokens/request.ts (getHandshakeRedirectLoopMessage)
- @clerk/backend, tokens/handshake.ts (checkAndTrackRedirectLoop)
- Clerk javascript, pull request 8636: Improve satellite auth redirect failures
- Clerk, Deploy to production
- Clerk, Instances and environments
- Clerk, Deploying a Clerk app to Vercel
- Vercel, Environment variables
Browse all launch guides · Read resources · How to make and check your next change
FAQ
FAQ for builders before launch
Why is there no error page in the browser?
The SDK logs the message with console.log and then treats the request as signed out. In the local run, the fourth request returned a signed-out result with no redirect, so you see your sign-in page, not an error.
Does the loop count survive a refresh?
Only for about two seconds. The counter cookie is set with Max-Age=2, per handshake.ts, so a refresh after that starts again from zero.
Why does my log show the satellite text instead?
Clerk pull request 8636, merged on 2026-07-08, made the SDK print the satellite text when the redirect reason is SatelliteCookieNeedsSyncing. Other loops keep the session-refresh text.