Skip to content

ReferenceError: self is not defined in Next.js 14 App Router production builds - OpenTelemetry incompatibility #1569

@sescanella

Description

@sescanella

Bug report

  • I confirm this is a bug with Supabase, not with my own application.
  • I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

ReferenceError: self is not defined occurs in production builds when using @supabase/supabase-js in Next.js 14 App Router server-side contexts. The error manifests in the generated vendors.js bundle at line 1, preventing successful production builds while development mode works perfectly.

The issue is caused by OpenTelemetry auto-instrumentation in Supabase packages that contains browser-specific self references, which are incompatible with Node.js server environments.

To Reproduce

Steps to reproduce the behavior:

  1. Create a Next.js 14 application with App Router
  2. Install @supabase/supabase-js (any version from 2.39.0 to 2.57.4)
  3. Use createClient in any Server Component:
    import { createClient } from '@supabase/supabase-js'
    
    export default async function ServerComponent() {
      const supabase = createClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
      )
      // Any server-side usage (even just importing triggers the error)
      return <div>Server Component</div>
    }
  4. Run npm run build
  5. Error occurs: ReferenceError: self is not defined in vendors.js:1

Minimal reproduction repository: https://github.com/[will-create-if-needed]

Expected behavior

Production builds should complete successfully without self is not defined errors. The Supabase client should work in server-side contexts just as it does in development mode (npm run dev).

Screenshots

Build error output:

ReferenceError: self is not defined
    at vendors.js:1:1
    at Module evaluation
    at __webpack_require__

System information

  • OS: Windows 11
  • Browser: N/A (server-side error)
  • Version of supabase-js: 2.57.4 (also tested: 2.39.0, 2.43.4)
  • Version of @supabase/ssr: 0.7.0 (also tested: 0.3.0)
  • Version of Node.js: 20.10.0
  • Next.js version: 14.2.5
  • Build environment: Production (npm run build)

Additional context

Investigation Summary

We conducted an exhaustive investigation with 13 different resolution approaches including:

  • Version pinning to pre-OpenTelemetry versions
  • Webpack configuration modifications (DefinePlugin, externals, entry points)
  • Runtime polyfill injection
  • Server-side environment isolation

All approaches failed because the self reference occurs at module loading time in the vendor bundle, before any polyfills can be applied.

Root Cause Analysis

The issue stems from OpenTelemetry auto-instrumentation being deeply embedded in the Supabase package ecosystem. Even older versions (2.39.0) contain transitive dependencies with browser-specific code.

Business Impact

  • ✅ Development mode (npm run dev): Fully functional
  • ❌ Production builds (npm run build): Completely blocked
  • ✅ All other functionality: TypeScript, linting, testing work normally

Dependency Chain

@supabase/[email protected] → @supabase/[email protected]
└── OpenTelemetry auto-instrumentation with 'self' references

Even downgraded:
@supabase/[email protected] → @supabase/[email protected]
└── @supabase/[email protected] (contains 'self' references)

Requested Resolution

We need server-safe builds of Supabase packages that either:

  1. Conditionally exclude OpenTelemetry instrumentation in server environments, or
  2. Use server-compatible alternatives to browser globals like self

This affects any Next.js 14+ App Router application using Supabase in Server Components, which is a common and recommended pattern.

Workarounds Attempted

All standard workarounds (webpack configuration, polyfills, version pinning) have been exhausted. The only current options are:

  1. Wait for upstream fix (preferred)
  2. Use development mode in production (not recommended)
  3. Fork and maintain custom Supabase builds (high maintenance overhead)

Full Investigation Report

Investigation Summary: 13 Failed Resolution Attempts

We conducted an exhaustive technical investigation with the following approaches:

Phase 1: Version Pinning Investigation (❌ Failed)

Strategy: Downgrade to pre-OpenTelemetry versions

  • Tested versions: @supabase/[email protected], 2.43.4, 2.57.4
  • Also tested: @supabase/[email protected], 0.7.0
  • Result: Even oldest supported versions contain self references in transitive dependencies
  • Key finding: @supabase/[email protected] contains browser-specific code

Phase 2: Webpack Configuration Approaches (❌ All Failed)

5 Different webpack strategies attempted:

  1. DefinePlugin Strategy:

    new webpack.DefinePlugin({
      'self': 'global',
      'self.webpackChunk_N_E': 'undefined'
    })
  2. Externals Strategy:

    config.externals.push({
      '@opentelemetry/api': 'commonjs @opentelemetry/api',
      '@opentelemetry/auto-instrumentations-node': 'commonjs @opentelemetry/auto-instrumentations-node'
    })
  3. Entry Point Injection:

    config.entry = async () => {
      const entries = await originalEntry()
      // Inject polyfills into all server entries
      return modifyEntries(entries)
    }
  4. Runtime Polyfill Creation:

    // lib/polyfills/server-runtime.js
    if (typeof self === 'undefined') {
      global.self = global
      global.window = global
      global.document = {}
      // ... comprehensive browser globals
    }
  5. Module Replacement Strategy:

    config.resolve.alias = {
      '@opentelemetry/api': false,
      '@opentelemetry/auto-instrumentations-node': false
    }

Critical limitation discovered: All webpack polyfills load AFTER the problematic vendor bundle executes.

Phase 3: Advanced Bundle Analysis (❌ Failed)

Deep technical investigation:

  • Bundle timing analysis: vendors.js executes independently at module loading time
  • Dependency tree mapping: OpenTelemetry present in multiple transitive dependencies
  • Module loading order: Vendor chunks load before any entry point modifications
  • Runtime environment isolation: Server context polyfills cannot intercept vendor execution

Root Cause Technical Analysis

DEPENDENCY CHAIN ANALYSIS:
@supabase/[email protected] → @supabase/[email protected]
├── @supabase/[email protected] (contains 'self' references)
├── @supabase/[email protected]
└── OpenTelemetry auto-instrumentation modules

BUNDLE EXECUTION ORDER:
1. vendors.js (contains OpenTelemetry code with 'self') ← FAILS HERE
2. webpack runtime
3. entry point polyfills (too late)

Technical Constraints Identified

  1. Module Loading Timing: Next.js vendor bundle executes before any webpack polyfills can be applied
  2. Transitive Dependencies: OpenTelemetry deeply embedded across Supabase package ecosystem
  3. Server Bundle Structure: vendors.js loads independently of entry point modifications
  4. Auto-instrumentation: OpenTelemetry code executes at module import time, not runtime

Environment Validation

✅ Development Mode (npm run dev):

  • All Supabase functionality works perfectly
  • TypeScript validation passes
  • All Zeus architecture patterns functional

❌ Production Mode (npm run build):

  • Consistent failure at vendors.js:1
  • Error: ReferenceError: self is not defined
  • Build process completely blocked

Attempted Workarounds Status

Approach Status Technical Reason for Failure
Version pinning ❌ Failed OpenTelemetry in older versions too
Webpack externals ❌ Failed Bundle loads before externals applied
DefinePlugin polyfills ❌ Failed Vendor bundle executes first
Runtime polyfills ❌ Failed Module loading vs runtime timing
Entry point injection ❌ Failed Vendors independent of entry points
Module aliasing ❌ Failed Deep transitive dependencies
Server environment isolation ❌ Failed Cannot intercept vendor execution

Architecture Impact Assessment

✅ Preserved Zeus Patterns:

  • TypedSupabaseClient interface maintained
  • Hybrid Client Pattern functioning in dev mode
  • Service Layer contracts intact (SERVICE-CONTRACTS.md compliance)
  • RLS policies and security layer unaffected

❌ Production Deployment Blocked:

  • Cannot generate production builds
  • Deployment pipeline blocked
  • Performance optimizations unavailable

Conclusion

This represents a fundamental incompatibility between Supabase's OpenTelemetry auto-instrumentation and Next.js 14 App Router server-side rendering. The issue requires upstream resolution from the Supabase team to provide server-compatible builds or conditional OpenTelemetry loading.

Technical evidence: 13 comprehensive approaches attempted, all standard Node.js/webpack workarounds exhausted, issue occurs at vendor bundle module loading level before any application-level polyfills can be applied.

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