Skip to content

Vite serves HTML for /@vite/client when resolved path contains # #22329

@AlwaysHC

Description

@AlwaysHC

Description

When a Vite project is opened through a symlink/junction path but Node resolves Vite internals to a real filesystem path containing #, Vite serves index.html for internal client module URLs such as /@vite/client and /@vite/env.

The browser then rejects /@vite/client as a JavaScript module because the response is text/html, breaking dev startup/HMR.

This is easy to hit on Windows when a project lives under a directory such as C# and is opened through a junction/symlink without #.

Reproduction

  1. Create a Vite app under a real path containing #, for example:

    mkdir D:\tmp\C#\vite-hash-path-repro
    cd D:\tmp\C#\vite-hash-path-repro
    npm create vite@latest . -- --template react-ts
    npm install
    npm run dev
  2. Open the served page and inspect the network response for:

    /@vite/client
    

Observed

/@vite/client returns the SPA fallback HTML:

HTTP 200
content-type: text/html
body starts with <!doctype html>

The browser reports:

Loading module from "http://127.0.0.1:5173/@vite/client" was blocked because of a disallowed MIME type ("text/html").

Expected

/@vite/client should return JavaScript:

HTTP 200
content-type: text/javascript

Local confirmation

In a project opened through a CSharp junction whose real path is D:\MyProgs\C#\AIWiki, the following finite Vite API test reproduces the problem:

resolved=D:\MyProgs\C#\AIWiki\src\frontend\node_modules\vite\dist\client\client.mjs
/@vite/client 200 text/html <!doctype html>
/@vite/env 200 text/html <!doctype html>

Starting Node with symlink preservation for both imports and the CLI main module avoids the bad real path and fixes the response:

resolved=D:\MyProgs\CSharp\AIWiki\src\frontend\node_modules\vite\dist\client\client.mjs
/@vite/client 200 text/javascript import "/node_modules/vite/dist/client/env.mjs";
/@vite/env 200 text/javascript //#region src/client/env.ts

Workaround:

{
  "scripts": {
    "dev": "node --preserve-symlinks --preserve-symlinks-main ./node_modules/vite/bin/vite.js"
  }
}

Using only --preserve-symlinks is not enough when launching Vite by path, because Node still resolves the Vite CLI entrypoint as the main module unless --preserve-symlinks-main is also set.

Suspected cause

Vite already warns that project/config paths containing #, ?, or * may not work.

In Vite 7.3.2, the internal client aliases are generated as raw /@fs/... URLs from CLIENT_ENTRY and ENV_ENTRY. When the resolved absolute path contains #, the browser treats the rest of the path as a URL fragment, so Vite cannot resolve the internal client module and eventually falls through to the HTML fallback.

The fix may be to encode URL-reserved characters, for example # to %23, when generating internal /@fs/... URLs for Vite client/env entries, or otherwise avoid browser-visible raw filesystem paths for these internal aliases.

Environment

vite: 7.3.2
node: 24.11.1
OS: Windows 11

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions