Short answer
Let supabase-js manage the session: don’t copy the access token into your own headers or storage. In React Native, start and stop auto refresh with AppState so the token refreshes when the app returns to the foreground. On the server, create a client per request from the user’s cookies instead of reusing a token. Raising the JWT expiry only hides the bug.
Why the token wasn’t refreshed, and the fix
01
A token copied into a header or variable
“Access tokens are designed to be short lived, usually between 5 minutes and 1 hour while refresh tokens never expire but can only be used once.” Supabase, User sessions
Creating the client with a fixed Authorization header, or passing an access token you saved earlier, sends the same token after it expires. Use the client’s own session, or read session.access_token fresh each time you need it.
02
A React Native app returning from the background
“You should hook into your platform's foreground indication mechanism and call these methods appropriately to conserve resources.” Supabase, startAutoRefresh
On mobile the OS can pause your app’s JavaScript while it is in the background, so a refresh due during that time is missed. Supabase’s docs say to hook into the platform’s foreground signal: start auto refresh when the app is active and stop it in the background.
import { AppState } from 'react-native'; AppState.addEventListener('change', (state) => { if (state === 'active') supabase.auth.startAutoRefresh(); else supabase.auth.stopAutoRefresh(); });03
A browser tab in the background
“On browsers the refresh process works only when the tab/window is in the foreground to conserve resources as well as prevent race conditions and flooding auth with requests.” Supabase, startAutoRefresh
In browsers, the refresh runs only while the tab is visible. Requests from a background tab, a worker or a long-running loop can go out with an old token. Refresh when the tab becomes visible again, before you send requests.
04
Server code reusing one user token
A server, cron job or Edge Function that stores a user’s access token fails once it expires. Create a client per request from the incoming cookies or Authorization header. For background jobs that act on their own, use a server-side secret key and scope the queries yourself, since that key bypasses RLS.
05
What PGRST303 and PGRST301 mean
“JWT claims validation or parsing failed.” Supabase, PostgREST error codes (PGRST303)
PostgREST returns 401 with the message JWT expired when the token’s exp claim is in the past. Current PostgREST reports it as PGRST303; versions before 13 reported PGRST301. Either way the fix is a fresh token, not a database change.
Checked against the vendor’s docs on : Supabase docs: User sessions; Supabase reference: startAutoRefresh; Supabase docs: PostgREST error codes.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
Related: Supabase Invalid Refresh Token logouts · Supabase JWSError JWSInvalidSignature · Supabase logs users out on refresh · Supabase Email link is invalid or has expired