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
-
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
-
Open the served page and inspect the network response for:
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
Description
When a Vite project is opened through a symlink/junction path but Node resolves Vite internals to a real filesystem path containing
#, Vite servesindex.htmlfor internal client module URLs such as/@vite/clientand/@vite/env.The browser then rejects
/@vite/clientas a JavaScript module because the response istext/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
Create a Vite app under a real path containing
#, for example:Open the served page and inspect the network response for:
Observed
/@vite/clientreturns the SPA fallback HTML:The browser reports:
Expected
/@vite/clientshould return JavaScript:Local confirmation
In a project opened through a
CSharpjunction whose real path isD:\MyProgs\C#\AIWiki, the following finite Vite API test reproduces the problem:Starting Node with symlink preservation for both imports and the CLI main module avoids the bad real path and fixes the response:
Workaround:
{ "scripts": { "dev": "node --preserve-symlinks --preserve-symlinks-main ./node_modules/vite/bin/vite.js" } }Using only
--preserve-symlinksis not enough when launching Vite by path, because Node still resolves the Vite CLI entrypoint as the main module unless--preserve-symlinks-mainis 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 fromCLIENT_ENTRYandENV_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