Fix AuthSessionMissingError: Auth session missing!
Supabase

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.

8 min read
Fix AuthSessionMissingError: Auth session missing!

Photo by FLY:D on Unsplash

plaintext
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:

js
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:

js
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:

js
// 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:

js
// 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:

jsx
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:

js
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:

js
const store = await cookies();
console.log('[auth] cookies seen:', store.getAll().map((c) => c.name));
  • No sb-…-auth-token cookie 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).

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

|

Have more questions? Contact us

Written by

Mahdi Br
Mahdi Br

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — no fluff

RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.