Not a bug. Refresh tokens are single use: using one returns a new pair and the old one is dead. If two things refresh at the same moment with the same old token, the second one gets 'Already Used' and Supabase treats it as possible theft and ends the session.
In Next.js the usual causes:
- No middleware, so every Server Component tries to refresh on its own, and can't save the result to cookies. Next request uses the old token again.
- Your
refreshSession()interval racing the automatic refresh of the browser client. Remove it; the client already refreshes by itself.
Fix: one middleware that calls supabase.auth.getUser() (the official updateSession example), no manual refreshes anywhere, and set your JWT expiry back to the default. Long-lived access tokens are a security trade-off you don't need.