Skip to content

Importing next/headers in next.config.ts causes workUnitAsyncStorage invariant error in pnpm workspace monorepo #90669

@matthewvolk

Description

@matthewvolk

Link to the code that reproduces this issue

https://github.com/bigcommerce/catalyst/tree/CATALYST-1791-upgrade-next

To Reproduce

  1. Clone the repository and switch to the reproduction branch:
    git clone https://github.com/bigcommerce/catalyst.git
    cd catalyst
    git checkout CATALYST-1791-upgrade-next
    pnpm install
    
  2. In core/next.config.ts, change line 6 from:
    import { configClient } from "./client/config-client";
    
    to:
    import { client as configClient } from "./client";
    
    The key difference between the two imports is that core/client/index.ts has import { headers } from 'next/headers' at line 2, while core/client/config-client.ts is a duplicate that avoids that import.
  3. Run the dev server:
    pnpm run dev
    
  4. Visit http://localhost:3000 → 500 error with:
    InvariantError: Invariant: Expected workUnitAsyncStorage to have a store.
    
  5. Revert the import back to config-client → app works normally.

Current vs. Expected behavior

Current behavior:

When next/headers is imported (even transitively, even if headers() is never called) during next.config.ts resolution in a pnpm workspace monorepo, all page renders fail with:

InvariantError: Invariant: Expected workUnitAsyncStorage to have a store. This is a bug in Next.js.

This worked fine in Next.js 15.

Expected behavior:

Importing next/headers at the top level of a module used in next.config.ts should not poison the AsyncLocalStorage context for the runtime. If the import is problematic, Next.js should either handle it gracefully or surface a clear error during config resolution rather than failing silently at render time.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:53:05 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T6020
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 24.13.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: 10.12.4
Relevant Packages:
  next: 16.1.6 // Latest available version is detected (16.1.6).
  eslint-config-next: 15.5.10
  react: 19.1.5
  react-dom: 19.1.5
  typescript: 5.8.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Module Resolution

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

This issue only reproduces in a pnpm workspace monorepo with a sufficiently complex dependency graph. We have been unable to reproduce it in any minimal environment:

  • A non-monorepo standalone project
  • A minimal pnpm workspace monorepo matching the same directory structure
  • Adding next-intl/plugin, middleware, instrumentation, and locale routing to match our setup
  • Matching exact pnpm version (10.12.4) and Node.js version (24.13.0)
  • Using --preserve-symlinks / --preserve-symlinks-main flags

Our debugging suggests the issue is related to Module._pathCache being populated with the symlink path (e.g., core/node_modules/next/headers.js) during next.config.ts resolution, while the runtime resolves to the real path (.pnpm/.../next/headers.js). This results in two separate AsyncLocalStorage singleton instances — one used by the config loading phase and one by the SSR runtime — so getStore() returns undefined during rendering.

This worked fine in Next.js 15 with the same monorepo structure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Module ResolutionModule resolution (CJS / ESM, module resolving).

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions