# Fix: AuthSessionMissingError: Auth session missing!

supabase-js could not find a session for this client, so `auth.getUser()` had no access token to send. In Next.js this almost always means the server-side client cannot read the session cookie: it was created with plain `createClient` instead of `@supabase/ssr`, the user signed in with a client that stores the session in `localStorage`, or no proxy (middleware) refreshes the cookie. For a visitor who is simply signed out, the error is expected and should be treated as "no user".

| Fact | Detail |
|---|---|
| Error | `AuthSessionMissingError: Auth session missing!` |
| Where you see it | The `error` returned by `supabase.auth.getUser()`, server logs, the browser console |
| Usual cause | A server client that cannot read the `sb-<project_ref>-auth-token` cookie |
| Usual fix | Create clients with `@supabase/ssr` (`createServerClient` / `createBrowserClient`) and add the proxy that refreshes the session |
| BugTape catches it | Only when your code throws it. `getUser()` returns the error without throwing |

## What the error means

`AuthSessionMissingError` comes from Supabase Auth (`auth-js`, bundled in supabase-js). Its message is `Auth session missing!` and its status is 400. `getUser()` returns it as `{ data: { user: null }, error }` when the client has no access token. Other calls that need a signed-in user, such as `updateUser()` and `refreshSession()`, give back the same error when there is no session.

The client finds the session in its storage. In the browser, plain supabase-js uses `localStorage`. On a server there is no `localStorage`, so the session has to come from the request cookies. `@supabase/ssr` provides clients that read and write the session as cookies, named `sb-<project_ref>-auth-token` by default. If the server client cannot see that cookie, it has no session, and `getUser()` returns this error even for a signed-in user.

## Common causes

1. **The visitor is signed out.** Nothing is broken. Code that throws on any `getUser()` error turns every anonymous visit into an exception.
2. **The server uses `createClient` from `@supabase/supabase-js`.** It has no cookie storage, so Server Components, Route Handlers and Server Actions never see the session.
3. **The user signed in with a `localStorage` client.** The browser client was plain `createClient`, so the session never reached a cookie. Use `createBrowserClient` from `@supabase/ssr`.
4. **No proxy refreshes the session.** Server Components cannot write cookies. Without the proxy, an expired access token is not refreshed and the server stops seeing a valid session. On Next.js 15 and earlier the file must be `middleware.ts` with `export async function middleware`. A `proxy.ts` file is never called there.
5. **A server-side `fetch` to your own API.** A Server Component that calls `fetch('/api/…')` does not forward the browser's cookies, so the Route Handler sees no session.
6. **The proxy returns a new response without the cookies.** Returning a fresh `NextResponse` drops the refreshed cookies, and the user looks signed out on the next request.

## How to fix it

### Create the server client with `@supabase/ssr`

```bash
npm install @supabase/ssr @supabase/supabase-js
```

```ts
// lib/supabase/server.ts
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_PUBLISHABLE_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. The proxy refreshes the session.
          }
        },
      },
    },
  );
}
```

Create a new client in every request. Do not keep it in a module-level variable.

### Use the cookie-based browser client

```ts
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr';

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
  );
}
```

Sign in with this client. The session is then stored in cookies that the server can read.

### Add the proxy that refreshes the session

On Next.js 16, the file is `proxy.ts`. On Next.js 15 and earlier, name it `middleware.ts` and export `middleware` instead of `proxy`. The Supabase code is the same.

```ts
// proxy.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';

export async function proxy(request: NextRequest) {
  let supabaseResponse = NextResponse.next({ request });

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll();
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value));
          supabaseResponse = NextResponse.next({ request });
          cookiesToSet.forEach(({ name, value, options }) =>
            supabaseResponse.cookies.set(name, value, options),
          );
        },
      },
    },
  );

  // Do not run code between createServerClient and getClaims().
  await supabase.auth.getClaims();

  return supabaseResponse; // return this object, or copy its cookies onto yours
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
};
```

### Treat a missing session as signed out

Check for this error by type and show the signed-out state. Report other auth errors.

```tsx
import { isAuthSessionMissingError } from '@supabase/supabase-js';
import { redirect } from 'next/navigation';
import { createClient } from '@/lib/supabase/server';

export default async function AccountPage() {
  const supabase = await createClient();
  const { data, error } = await supabase.auth.getUser();

  if (error && !isAuthSessionMissingError(error)) throw error; // a real failure
  if (!data.user) redirect('/login');

  return <p>Signed in as {data.user.email}</p>;
}
```

Supabase now recommends `getClaims()` to protect pages. It verifies the token's signature and returns `data: null` with no error when there is no session, so the signed-out case needs no special handling. Never trust `getSession()` on the server, because it reads the cookie without checking it.

### Call Supabase directly instead of fetching your own API

In a Server Component, create the server client and query Supabase there. If you must call a Route Handler from the server, forward the cookie header yourself.

## How to catch it in production with BugTape

`getUser()` returns this error and does not throw it, so BugTape sees nothing unless your code throws or reports it. That is the right default for signed-out visitors. The case worth catching is a signed-in user who suddenly looks signed out, which shows up as redirects to login rather than as an error.

On the server, the Node.js SDK reports errors thrown from pages, Route Handlers, Server Actions and the proxy through Next.js `onRequestError`, on the Node.js runtime. Proxy runs on Node.js by default in Next.js 16. Middleware on the Edge runtime in older versions is not supported. To report the cases where a session should exist, call `captureException` with a short context:

```ts
// instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') (await import('@bugtape/node')).init({ service: 'web' });
}

export async function onRequestError(...args: Parameters<typeof import('@bugtape/node').captureRequestError>) {
  if (process.env.NEXT_RUNTIME === 'nodejs') await (await import('@bugtape/node')).captureRequestError(...args);
}
```

```ts
import * as bugtape from '@bugtape/node';
import { isAuthSessionMissingError } from '@supabase/supabase-js';
import { cookies } from 'next/headers';

const { data, error } = await supabase.auth.getUser();
const hasAuthCookie = (await cookies()).getAll().some((c) => c.name.includes('-auth-token'));

if (error && isAuthSessionMissingError(error) && hasAuthCookie) {
  bugtape.captureException(error, { extra: { where: 'account page' } }); // cookie present, session not read
}
```

In the browser, an `AuthSessionMissingError` that your code throws from a click handler or an unhandled promise is reported automatically. The browser SDK has no `captureException`. Call `reportError(error)` to send one you handled. It fires the same window `error` event the SDK listens for.

Repeats group into one issue with a count. Set `release` so a fix that does not hold reopens the issue as a regression. Your coding agent can read the issue over MCP (`npx -y bugtape connect claude`, or `cursor` or `codex`) and propose the change. You review the pull request.

## FAQ

### Why does getUser() work in the browser but return Auth session missing on the server?

The browser client found the session in its own storage. The server client only sees cookies. If you signed in with plain `createClient`, the session is in `localStorage` and the server never receives it. Switch both clients to `@supabase/ssr`.

### Should I use getUser(), getSession() or getClaims() on the server?

Use `getClaims()` to protect pages and data. It verifies the token. `getUser()` also verifies, by calling the Auth server. Do not rely on `getSession()` on the server, because it reads the cookie without checking it, and anyone can forge a cookie.

### Is AuthSessionMissingError a bug?

Not for a signed-out visitor. It means there is no session. Treat it as a null user. It is a bug when a signed-in user gets it, which points at cookies not reaching the server or the session not being refreshed.

### Why are users signed out at random?

Usually because the session is refreshed in two places, or the proxy returns a response without the refreshed cookies. Supabase revokes the session when a used refresh token is presented again outside its short reuse window. Refresh only in the proxy and return the response that `setAll` built.

## Sources

- [auth-js source: errors.ts](https://github.com/supabase/supabase-js/blob/master/packages/core/auth-js/src/lib/errors.ts) and [GoTrueClient.ts](https://github.com/supabase/supabase-js/blob/master/packages/core/auth-js/src/GoTrueClient.ts) (message, status 400, `getUser()` and `getClaims()` behaviour), checked 2 Nov 2026.
- [Supabase: Creating a Supabase client for SSR with Next.js](https://supabase.com/docs/guides/auth/server-side/nextjs) (proxy, cookie name, `getClaims()`, `getSession()` warning, refresh token reuse), checked 2 Nov 2026.
- [Next.js with-supabase example](https://github.com/vercel/next.js/tree/canary/examples/with-supabase) (`lib/supabase/server.ts`, `client.ts`, `proxy.ts`), checked 2 Nov 2026.
- [Next.js: proxy.js](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) (renamed from middleware in v16, Node.js runtime by default), checked 2 Nov 2026.

## Related errors

- [Supabase row-level security violation](/guides/supabase-rls-violation/): `new row violates row-level security policy for table`
- [supabaseUrl is required](/guides/supabase-url-is-required/): `supabaseUrl is required.`
- [Next.js Server Components render error](/guides/nextjs-server-components-render-error/): `An error occurred in the Server Components render. The specific message is omitted in production builds`
- [Firebase missing or insufficient permissions](/guides/firebase-missing-or-insufficient-permissions/): `FirebaseError: Missing or insufficient permissions.`

All guides: [Fix an error](/guides/).

[Start free](/console/register?utm_source=bugtape_seo&utm_campaign=error-guides). Free plan · No credit card · Set up in minutes. Or [try the live demo](/demo/?from=landing&start=1#capture) first.
