Skip to content

ReferenceError: window is not defined crash in GoTrueClient _initialize() on server-side environments #1073

@gonchantech

Description

@gonchantech

Describe the bug

When using createServerClient from @supabase/ssr on the server-side (Node.js), the internally initialized GoTrueClient calls _initialize() in its constructor. This method attempts to access window.location.href, which throws a ReferenceError in non-browser environments and causes the application to crash.

While the method recoverAndRefresh() is designed to work in server-side environments (it can load session information from cookies or other configured storage), _initialize() assumes a browser environment and throws an exception before recoverAndRefresh() is ever reached.

In practice, other methods may recover the session later, so this doesn’t immediately break all functionality. However, this still represents an inconsistent design: recoverAndRefresh() is server-compatible, yet it's gated behind a browser-only execution path. The fact that isBrowser() is checked later inside the same method also implies that this code was intended to run in both environments.

To Reproduce

I've created a reproducible example with Express. You can check the whole code here. This is not a bug specific to Express; the crash stems from the way @supabase/auth-js is structured internally.

1. Install Supabase

npm install @supabase/ssr
Note: This issue is in @supabase/auth-js, not @supabase/ssr. However, I recommend using @supabase/ssr to simplify cookie-based session storage configuration on the server.

2. Enable debug logging in _initialize()

In @supabase/auth-js/src/GoTrueClient.ts, modify the catch block inside _initialize() around line 383 to add this._debug() to observe the crash:

await this._recoverAndRefresh()
return { error: null }
} catch (error) {`
  this._debug('#_initialize()', 'error', error) // add this
  if (isAuthError(error)) {
        return { error }
  }

3. Minimal Express route to reproduce

const express = require("express");
const { createServerClient } = require("@supabase/ssr");
const router = express.Router();

router.get("/issue", async (req, res) => {
  const SUPABASE_PUBLIC_URL = process.env.SUPABASE_PUBLIC_URL;
  const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY;

  if (!SUPABASE_PUBLIC_URL || !SUPABASE_ANON_KEY) {
    return res.status(500).json({ error: "Missing environment variables" });
  }

  const supabase = createServerClient(
    SUPABASE_PUBLIC_URL,
    SUPABASE_ANON_KEY,
    {
      auth: {
        debug: true, // important for tracing the issue
      },
      cookieOptions: {
        httpOnly: true,
        secure: process.env.NODE_ENV === "production",
        sameSite: "lax",
      },
      cookies: {
        getAll() {
          return Object.entries(req.cookies).map(([name, value]) => ({
            name,
            value: value || "",
          }));
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => {
            res.cookie(name, value, options);
          });
        },
      },
      cookieEncoding: "base64url",
    }
  );

  res.json({ success: true, message: "Supabase initialized" });
});

module.exports = router;

4. Send a request

Request the /issue route. You’ll observe that _initialize() throws a ReferenceError: window is not defined.

Expected behavior

In Node.js environments, _initialize() should not attempt to access window.location.href. It should conditionally access that property only when isBrowser() is true. This would allow the server-side recoverAndRefresh() logic to work as intended.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • OS: Microsoft Windows 11 Home
  • Runtime: Node.js v22.13.1
  • Packages:
{
   "@supabase/ssr": "^0.6.1",
    "@supabase/supabase-js": "^2.50.0",
    "cookie-parser": "^1.4.7",
    "dotenv": "^16.5.0",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.2"
}

Additional context

none.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions