Refresh Token Not Found (Supabase Auth)

Filed under Supabase & databases

Most likely causeAbout 10 min · 6 steps

Your app presented a refresh token that Supabase had already rotated away — usually a second tab or client racing, or a session copy your code saved and replayed.

Sign out through the client, not by hand
Clear the stale stored session and sign in fresh
Use one shared client; never store tokens yourself

Fastest route

Give your AI this prompt

Copy it, replace the bracketed part with your error, and send it.

Preview the prompt
I am using Supabase Auth and getting "Invalid Refresh Token: Refresh Token Not Found". It happens [on page load / after the app sits open a while / when I have multiple tabs open / right after login — pick what fits].

Please: 1) search the project for every place a Supabase client is created and reduce browser code to one shared client, 2) find any code that stores, copies, or replays a session, access token, or refresh token itself, and remove it so only the client manages the session, 3) check that sign-out calls supabase.auth.signOut() and that nothing calls refreshSession() on a timer or on every page focus, 4) make refresh failures with this error route the user to login cleanly instead of retrying.

Constraints: do not fix this by turning off token rotation or auto-refresh, do not store tokens in my own database or extra storage, and do not put the service role key in browser code. Make one change at a time and tell me how to test it, including leaving the app open past the one-hour mark.

Fix it yourself

Follow the repair

  1. Sign out properly, through the client. Wherever your app has a sign-out button, it should call supabase.auth.signOut() — that tells Supabase to retire the session's tokens and clears the stored copy. If users have been "signed out" by code that merely deletes a cookie or clears a variable, dead sessions pile up and this error keeps visiting. If you cannot even load the app without hitting the error, use the manual clear in the next step first.
  2. Clear the stale stored session by hand. In the browser, open developer tools, go to Application (Chrome) or Storage (Firefox), open Local Storage for your site, and delete the key that looks like sb-xxxxxxxx-auth-token — that is the stored session. Reload and sign in again. A fresh login starts a brand-new token chain, which is exactly what you want: you are not repairing the old chain, you are replacing it.
  3. Reduce the app to one client. Search the codebase for createClient and createBrowserClient. Browser code should import one shared client from one file. Every extra client is another hand reaching for the same token, and the refresh race between them is the single most common self-inflicted cause of this error. The same rule applies across the server boundary: in Next.js, let the @supabase/ssr browser client, server client, and middleware pass the session through cookies as designed, rather than copying tokens between them yourself.
  4. Never store or replay the session yourself. Search for code that saves a session, an access token, or a refresh token into your own variables, database, or extra storage keys — tutorial patterns do this a lot. Delete it. The client already persists the session in the right place and keeps it current; any copy you keep is a future "not found" waiting for the next rotation.
  5. Tame the multi-tab race. If your app is routinely open in several tabs, know that the official client coordinates refresh through storage, but tabs holding an already-rotated token can still trip this error once before recovering. What you can control: do not add your own refresh timers, do not call refreshSession() on a schedule or on every page focus "just in case", and handle the error gracefully — when a refresh fails with this message, treat it as signed out and route to login instead of retrying in a loop, which just hammers the server with a dead token.
  6. Sign in fresh and watch it stay fixed. After the clean-out, sign in and use the app normally, then leave a tab open past the point where the access token expires (about an hour by default) and confirm it quietly keeps working. Open a second tab and use both. If the error still returns with a single client and no hand-saved session, note exactly what was open when it struck — that detail separates a remaining race in your app from an outside cause like a restored browser profile carrying an ancient token.
Why this happensOptional explanation

When a user signs in, Supabase issues a refresh token whose job is to mint new access tokens. To keep a stolen token from being useful for long, Supabase rotates it: each time a refresh succeeds, the token you used is retired and a new one takes its place. Only the newest token in the chain works. Present any older link in the chain and the server does not find it — hence the blunt error message.

Rotation turns "two copies of the session" into an error generator. The classic setup is two browser tabs: both hold the session, tab A refreshes and rotates the token, then tab B wakes up and refreshes with the copy it was holding — now invalid — and gets this error, sometimes logging the user out in the process. AI-built apps add their own versions: a second Supabase client created in another file with the same storage (both race to refresh, one loses), code that saves the session object into its own storage and restores it later, or server and browser both trying to refresh the same session. Supabase also treats reuse of an already-rotated token as a possible theft signal, so a race can escalate: instead of one failed request, the whole session family gets revoked and everyone is signed out. The error also appears innocently — after restoring a browser backup, switching devices, or when an old tab from days ago reloads with a long-dead token.

  • Two tabs, one token: the same session open in multiple tabs or windows that refresh at different times.
  • Duplicate clients: more than one Supabase client sharing the same storage, racing to refresh first.
  • Hand-saved sessions: code that stores the session or refresh token itself and replays an old copy.
  • Stale leftovers: an old login's data still sitting in browser storage, or a days-old tab waking up and trying its luck.

Quick answers

Why does having two tabs open cause this?

Because refresh tokens rotate: each successful refresh retires the old token and issues a new one. A second tab or client holding the pre-rotation copy presents a token the server no longer has, and it reports exactly that — not found.

Does this mean someone stole my users' tokens?

Probably not. The overwhelmingly common causes are your own app racing itself — duplicate clients, multiple tabs, a hand-saved session. Supabase does treat reuse of a rotated token as a possible theft signal and may revoke the session, which is a reason to fix the race, not a sign you were attacked.

What is the fastest way to make it stop?

Sign out with supabase.auth.signOut(), delete the stored session key (sb-...-auth-token) from browser local storage, reduce the app to one shared client, remove any code that stores tokens itself, and sign in fresh to start a clean token chain.

One fix, every Friday.

One real error, explained in plain English, with the fix — in your inbox every Friday. No spam, no jargon, unsubscribe anytime.