Fix guides / Supabase: "JWT expired" (PGRST301)
How to fix Supabase "JWT expired"
Everything works, then after about an hour requests fail with JWT expired (PGRST301) or a 401. Refreshing the page fixes it for a while. The user's access token expired and your app kept using the old one.
Why it happens
Supabase access tokens last one hour by default. The supabase-js client refreshes them automatically, but only if you let it. AI-built apps often break this by:
- Copying the access token into their own state, localStorage key, or an
Authorizationheader, then reusing that stale string. - Creating a new client with
autoRefreshToken: falseorpersistSession: false. - Server code (Next.js, Edge Functions) that reads the token from a cookie but never writes the refreshed one back.
- A computer clock that is badly wrong, so tokens look expired.
Fix 1: use one client and let it manage tokens
// lib/supabase.ts, import this everywhere
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(URL, ANON_KEY, {
auth: { autoRefreshToken: true, persistSession: true },
});
Remove any code that saves session.access_token yourself. When you need a token for your own API, ask for it at call time: const { data } = await supabase.auth.getSession();
Fix 2: Next.js server side
Use @supabase/ssr with a middleware that calls supabase.auth.getUser() on each request and sets the returned cookies on the response. That is what refreshes the session for server components.
Fix 3: Edge Functions
Pass the user's current token from the client on each call (supabase.functions.invoke does this for you) instead of a token stored at login.
Check the fix
- In Authentication settings, temporarily set a short JWT expiry if your plan allows it, or leave a tab open for over an hour.
- Use the app again; requests should succeed without a reload.
Quick Fix, $49Full Rescue, $149Ask first
Last updated 2026-10-21.