Skip to content

Dependency Optimizer: re-optimization can flip which duplicated chunk is "canonical" for a module, breaking module singletons (duplicate React on a cold-started dev server) #1273

Description

@uhyo

Describe the bug

🤖 AI Disclosure: Claude Code has written up majority of this issue and most of the reproduction README. I, as a human, still take responsibility for it.

Summary

When two optimizeDeps.include entries resolve to the same module (e.g. react and @vitejs/plugin-rsc > react, where @vitejs/plugin-rsc can be any package that peer-depends on react), the optimizer emits two chunks for it — one holds the real module, the other re-exports it. Which one is "canonical" vs. the re-export is not stable across optimizer passes: a re-optimization can swap them.

chunk pass 1 (cold) pass 2 (after re-optimization)
react.js canonical alias → @vitejs_plugin-rsc_n_react.js
@vitejs_plugin-rsc_n_react.js alias → react.js canonical

If that re-optimization is triggered after some modules have already been served (a dependency discovered at request time), a consumer loaded before the swap and one loaded after end up bound to two different real instances of the module. For React this is Invalid hook call / Cannot read properties of null (reading 'useState'); the page then auto-reloads (optimized dependencies changed. reloading) and the error vanishes — so it's cold-start-only, first-render-only.

Detailed explanation can be found in the reproduction repository's README.

Context

I've been developing a React framework based on @vitejs/plugin-rsc. This issue originally surfaced as a framework bug, but a thorough investigation made us believe that this is a Vite issue.

The repro still uses @vitejs/plugin-rsc because it is the way we are aware of for reliably reproducing the issue, but we believe this issue is in Vite's Deps Optimization, hence a bug report in this repo.

The trigger path (via @vitejs/plugin-rsc): a "use client" package imported by a server component is reached on the browser through a request-time virtual:vite-rsc/client-package-proxy/<pkg> module that the optimizer's initial scan can't follow — so the package (and the React it imports) is discovered on the first render and forces the re-optimization.

Side Note: possible another form of this bug

Initially this bug surfaced as a different error like this:

The requested module '/node_modules/.vite/deps/react_jsx-runtime.js?v=...' does not provide an export named 't'

a severer ES Modules link time error. This version of error is not included in this bug report because (1) we couldn't reproduce this without involving our framework and (2) we are not sure whether they are truly same origin. Read more about this at uhyo/funstack-static#124 and uhyo/funstack-static#128.

Reproduction

https://github.com/uhyo/vite-rsc-deps-issue-repro/tree/repro/duplicate-react-cold-start

Steps to reproduce

pnpm install && rm -rf node_modules/.vite && pnpm dev
# open the URL; first load throws Invalid hook call, then auto-reloads

System Info

System:
    OS: Linux 6.18 Ubuntu 22.04.1 LTS 22.04.1 LTS (Jammy Jellyfish)
    CPU: (16) x64 AMD Ryzen 7 PRO 6850U with Radeon Graphics
    Memory: 12.37 GB / 15.01 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 24.13.1 - /home/uhyo/.volta/tools/image/node/24.13.1/bin/node
    Yarn: 4.1.1 - /home/uhyo/.volta/tools/image/yarn/4.1.1/bin/yarn
    npm: 11.4.2 - /home/uhyo/.volta/tools/image/npm/11.4.2/bin/npm
    pnpm: 10.30.1 - /home/uhyo/.volta/bin/pnpm
    Deno: 2.2.1 - /home/uhyo/.deno/bin/deno
  npmPackages:
    @vitejs/plugin-react: latest => 6.0.3 
    @vitejs/plugin-rsc: latest => 0.5.27 
    vite: ^8.0.16 => 8.1.0

Used Package Manager

pnpm

Logs

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions