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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions