Blank screen after login: Supabase onAuthStateChange deadlock
Guide · October 19, 2026
The user logs in. The spinner keeps spinning. The app never loads. No error in the console, no network failure. The login worked. Everything after it did not.
This is a known bug in supabase-js: if you await a Supabase API call inside the onAuthStateChange callback, the next Supabase call anywhere in your app hangs forever. Common in AI-generated code, especially from Lovable.
Primary source: Why is my Supabase API call not returning? Also see: onAuthStateChange reference.
Stuck on this right now? Paste your error and we'll email you the likely cause within 24 hours, free.
What it looks like
- User logs in (OAuth, magic link, email/password). Auth succeeds.
- The app shows a loading spinner indefinitely, or a blank screen.
- No error in the console. No 401, no timeout, no RLS denial.
- Refreshing the page after login sometimes works (session already exists, so
onAuthStateChangefires differently). - The problem reproduces every time you sign in fresh or clear cookies.
If you instead see the app redirect back to the login page or land on localhost, that is a different bug: login works in preview, breaks in production.
Why it happens
From the Supabase troubleshooting docs:
"There is currently a bug in supabase-js which results in a deadlock if any async API call is made in onAuthStateChange code. If a call is made in the handler then the next Supabase call anywhere using that client will hang and not return." Source: supabase.com/docs
Lovable and other AI code generators sometimes produce code like this:
supabase.auth.onAuthStateChange(async (event, session) => {
if (event === 'SIGNED_IN' && session) {
// This await deadlocks the client
const { data } = await supabase.from('profiles').select('*').eq('id', session.user.id).single();
setProfile(data);
}
});
The pattern looks fine. It fetches the user's profile when they sign in. But the await inside the callback triggers the bug. The .select() call never returns, and every subsequent Supabase call in the app also hangs.
Newer docs suggest that calling auth methods inside the callback may be fine now, but data-layer awaits (like supabase.from().select()) still match this troubleshooting page. The setTimeout(0) workaround remains the fix.
The fix
Move the async work out of the synchronous callback path. The simplest way is setTimeout(0):
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN' && session) {
// Defer to the next tick to avoid the deadlock
setTimeout(async () => {
const { data } = await supabase.from('profiles').select('*').eq('id', session.user.id).single();
setProfile(data);
}, 0);
}
});
The callback returns immediately. The Supabase call runs on the next event loop tick, outside the internal lock that causes the hang.
Alternative: queue work outside the callback
If you prefer not to use setTimeout, store the session in state and let a separate effect do the fetch:
const [session, setSession] = useState(null);
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, sess) => {
setSession(sess);
});
return () => subscription.unsubscribe();
}, []);
useEffect(() => {
if (!session) return;
supabase.from('profiles').select('*').eq('id', session.user.id).single()
.then(({ data }) => setProfile(data));
}, [session]);
The callback only sets state. The data fetch happens in a separate effect, which runs after the callback completes.
Quick checklist
- Search for
onAuthStateChangein your codebase. - Check if any callback uses
asyncor containsawaitwithsupabase.from()or similar data calls. - Wrap those calls in
setTimeout(() => { ... }, 0). - Test a fresh sign-in (clear cookies, sign in again) to confirm the fix.
Questions
Why does my Supabase app show a blank screen after login?
An async API call inside onAuthStateChange can deadlock supabase-js. The login succeeds, but the next Supabase call hangs forever. Defer the work with setTimeout(0) or move it outside the callback.
Can I use await inside onAuthStateChange?
Not for data-layer calls. Supabase documents a bug that deadlocks if any async API call is made in the handler. Auth methods may be fine in newer versions, but data calls still hang. Defer them.
How do I fix the onAuthStateChange deadlock?
Wrap the async work in setTimeout(() => { ... }, 0). This moves the call out of the synchronous callback path. The call runs on the next tick, avoiding the deadlock.
Why does this happen in Lovable apps?
Lovable (and other AI code generators) sometimes generate code that fetches user data immediately inside onAuthStateChange. The pattern looks reasonable, but it triggers the supabase-js deadlock.
Related guides: login works in preview, breaks in production (redirect/allowlist issues, not deadlocks). select returns empty array (RLS filtering, not hanging). saved but nothing saved (silent RLS denials).
App stuck after login? That is an auth fix. $149, you test the PR before you pay. No fix, no pay.
Get this fixed for $149Still stuck? Paste the error and we'll email you the likely cause, free.