Fix AuthSessionMissingError: Auth session missing!
`AuthSessionMissingError: Auth session missing!` is not a bug report — it is Supabase telling you, accurately, that the client you called had no session to work with. The hard part is that the same message covers a signed-out user, a server client that never received your cookies, a middleware that forgot to forward them, and a `getUser()` call that fired before the session was restored. Each has a different fix.
AuthSessionMissingError: Auth session missing!That string comes from @supabase/auth-js, not from the Supabase API. The SDK
raises it locally, before any network request, whenever a method that needs a
session cannot find one. Reading it as "Supabase is broken" sends you looking in
the wrong place; reading it as "this particular client object is empty" sends you
straight to the cause.
There are four ways a client ends up empty, and they need four different fixes.
First: which call produced it?#
The SDK is not consistent about how it surfaces this, and the inconsistency causes real bugs.
getUser() returns the error:
const { data, error } = await supabase.auth.getUser();
// data.user === null
// error?.name === 'AuthSessionMissingError'updateUser(), setSession() and refreshSession() throw it.
So this code is wrong in a way that will not show up until production:
try {
const { data } = await supabase.auth.getUser();
renderDashboard(data.user); // data.user is null, no exception was thrown
} catch (e) {
redirectToLogin(); // never runs
}Check the error value, or check data.user for null. A try/catch alone will
walk a signed-out visitor straight into your authenticated UI.
Cause 1: a server client built without cookies#
This is the App Router classic, and it accounts for most of the reports.
In the browser, the Supabase client persists the session in localStorage and
finds it there on the next call. On the server there is no localStorage and no
ambient state — a server-side client only knows what you hand it. If you create
it with the anon key and nothing else, it has no session by construction, and
every getUser() returns Auth session missing! no matter who is signed in.
The server client must be built from the incoming request's cookie store:
// utils/supabase/server.js
import { createServerClient } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options),
);
} catch {
// Called from a Server Component: middleware handles the refresh.
}
},
},
},
);
}Two details people trip on. cookies() must be awaited in current Next.js
versions — if you have older code destructuring it synchronously, that is a
separate error worth fixing at the same time. And the try/catch around
setAll is deliberate: Server Components cannot write cookies, so the write
throws there and middleware has to do the refreshing instead.
Full wiring, including the client/server split and the route handler cases, is in the complete Supabase session and middleware guide.
Cause 2: no middleware refreshing the session#
Access tokens are short-lived. The browser client refreshes them on its own timer; the server has no timer, so it depends on middleware running on each request to exchange the refresh token and write the new cookies back.
Without it the failure is time-dependent, which is why it reads as flaky: sign
in, everything works, come back an hour later and every server render reports
Auth session missing! while the browser tab still believes it is signed in.
The middleware must both read and write:
// middleware.js
import { createServerClient } from '@supabase/ssr';
import { NextResponse } from 'next/server';
export async function middleware(request) {
let response = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value));
response = NextResponse.next({ request });
cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options),
);
},
},
},
);
// This call is what performs the refresh. Do not remove it.
await supabase.auth.getUser();
return response;
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};Returning a response object that is not the one the cookie writer mutated is
the subtle way to break this: the refreshed cookies get written to an object you
then discard, and the browser never receives them. The symptom is identical to
having no middleware at all.
Cause 3: reading the session too early on the client#
On first paint the browser client has not finished restoring the session from
storage. A getUser() fired at module scope, or in a component body rather than
an effect, can run before the restore completes and report the session as
missing — then a re-render a moment later shows it present.
Subscribe rather than poll:
useEffect(() => {
supabase.auth.getUser().then(({ data }) => setUser(data.user));
const { data: sub } = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user ?? null);
});
return () => sub.subscription.unsubscribe();
}, []);One warning: do not await other Supabase calls inside that callback. It deadlocks the client and every later query hangs forever — the mechanism and the fix are in Supabase hangs after onAuthStateChange.
Cause 4: nobody is signed in#
The dull answer, and often the right one.
For a visitor who never signed in, or who just signed out, Auth session missing! is the correct and expected result. If it is filling your logs, the
problem is that your code treats a normal state as an exception:
const { data, error } = await supabase.auth.getUser();
if (!data.user) {
redirect('/login'); // not an error path — a branch
}
if (error && error.name !== 'AuthSessionMissingError') {
captureException(error); // this one is a genuine failure
}Filter the expected case out of your error reporting and the remaining occurrences become meaningful again.
A quick way to tell the causes apart#
Log one line in the failing server-side code path:
const store = await cookies();
console.log('[auth] cookies seen:', store.getAll().map((c) => c.name));- No
sb-…-auth-tokencookie at all — the browser never sent one. The visitor is signed out (cause 4), or the sign-in redirect never landed on your origin: see the Supabase auth redirect fix. - Cookie present, still missing on the server — the client was built without the cookie store (cause 1).
- Cookie present but stale, and it only fails after a while — middleware is missing or discarding the refreshed response (cause 2).
Related failures with different messages#
- Supabase "__cf_bm" Cookie Rejected for Invalid Domain: Fix Once a session exists, the next class of problem is what that session is allowed to read. An authenticated request that returns zero rows is not an auth problem at all — that is row level security filtering silently, covered in why RLS returns zero rows, and you can reproduce your own policy against real Postgres in the RLS Playground.
Two adjacent messages worth not confusing with this one: the
getUser() security warning, which is about
trusting getSession() on the server, and
session persistence failures,
where the session exists but does not survive navigation.
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Continue Reading
Supabase Hangs After onAuthStateChange: The Deadlock
Your app loads, the session arrives, and then every Supabase call after it hangs forever. No error, no rejected promise, no network request — the query simply never returns and the loading spinner stays up until someone reloads the page. If you fetch a profile inside your `onAuthStateChange` callback, this is a documented deadlock in supabase-js, and the fix is one line.
Supabase Auth Session Disappears on Refresh? Fix (2026)
Supabase auth sessions mysteriously disappearing after page refresh? Learn the exact cause and fix it in 5 minutes with this tested solution.
Supabase Auth Callback Redirect Not Working? Next.js Fix
Auth redirect not working after Supabase sign-in? Here are the three root causes and the exact fixes — callback route, redirect URL allowlist, and router.refresh().
Browse by Topic
Find stories that matter to you.
