"Supabase refresh_token_already_used in Flutter: Fix"
Fix supabase refresh_token_already_used in Flutter: why concurrent refreshes trigger it, the single in-flight refresh lock, and session restore on app resume.
You open your app, and instead of the home screen you land on the login page — signed out for no reason. Or worse: a crash log full of refresh_token_already_used . This is the supabase refresh_token_already_used flutter error, and the good news is it's not a mystery: Supabase refresh tokens are single-use and rotate on every refresh, so any concurrent or duplicate refresh attempt invalidates the token family and kills the session. This guide explains exactly why it happens (race conditions on token refresh), how to prevent it with a single in-flight refresh lock, and how to keep users logged in reliably across app restarts and backgrounding — with runnable Flutter code for supabase_flutter 2.18.x. *Last updated: October 3, 2026* What you'll learn Why refresh_token_already_used happens in Flutter (token rotation + races) The single in-flight refresh lock pattern that prevents duplicate refreshes Handling session state with onAuthStateChange and TOKEN_REFRESHED Refreshing tokens when the app resumes from the background Keeping users logged in after app restart (session persistence and restore) What the Supabase refresh token reuse interval means for your app Fixing the supabase refresh_token_already_used error in Flutter First, the mechanism. Supabase Auth issues a short-lived access token (JWT, ~1 hour) and a refresh token. Every time you refresh, the server issues a new pair and invalidates the old refresh token — this is token rotation, and it's by design (a stolen token becomes useless after one use). The problem: if two refresh requests fire at nearly the same time — an auto-refresh timer plus a manual refreshSession() call, or two getSession() calls racing — the first one succeeds and rotates the token, and the second one presents the now-invalid old…