#23585·Vite

Build fails when the HTML entry is a symlink to a file outside root (Bazel sandbox)

Author: u9gCreated Sep 25, 2026Updated Sep 25, 2026

Describe the bug

When the HTML entry inside root is a symlink to a file outside root, vite build fails:

[plugin vite:build-html]
RolldownError: The "fileName" or "name" properties of emitted chunks and assets must be strings that are neither absolute nor relative paths, received "../index.html".

This is the layout of a Bazel sandbox (rules_js): the working directory is a real directory, and every input file in it is a symlink to the real file elsewhere. #21955 reported it from Bazel and was closed as fixed by #22832, but that fix only realpaths root itself. Here root is already a real path; the entry file is what points elsewhere. The resolver (now the Rust vite resolve plugin in rolldown) turns <root>/index.html into its real path, and vite:build-html then names the asset with path.relative(config.root, id), which starts with ../:

https://github.com/vitejs/vite/blob/bbc88129c967a8b8eedebd93acc82bee63b86b2a/packages/vite/src/node/plugins/html.ts#L1101-L1108

Vite 7 (rollup) fails the same way outside Bazel; it worked inside Bazel only because rules_js patches fs.realpath in Node, which the Rust resolver bypasses.

Workarounds that build fine:

  • resolve: { preserveSymlinks: true }
  • root: fs.realpathSync(dirname(vite.config)) plus build.outDir: path.resolve(process.cwd(), 'dist'), so the output still lands in the sandbox

Both change resolution for the whole project to fix the name of one output file. I think the HTML output name should come from the entry's input path (<root>/index.html), not from the resolved module id. Either the vite resolve plugin could leave HTML entries unresolved to their real path, or vite:build-html could map the resolved id back to the configured input before computing the relative path. Happy to send a PR for whichever you prefer.

Reproduction

https://github.com/u9g/vite-symlinked-entry-repro

Steps to reproduce

bash
npm install
npm run repro

repro.sh creates sandbox/ containing a symlink to each project file and runs vite build from there. No Bazel needed.

System Info

bash
  System:
    OS: macOS 27.0
    CPU: (8) arm64 Apple M1
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.19.0
    npm: 11.17.0
  npmPackages:
    vite: ^8.3.1 => 8.3.1 (rolldown 1.2.11)

Used Package Manager

npm

Logs

bash
vite v8.3.1 building client environment for production...
transforming...
✓ 4 modules transformed.
rendering chunks...
✗ Build failed in 1.11s
error during build:
Build failed with 1 error:

[plugin vite:build-html]
RolldownError: The "fileName" or "name" properties of emitted chunks and assets must be strings that are neither absolute nor relative paths, received "../index.html".
    at error (node_modules/rolldown/dist/shared/logs-D6uA606S.mjs:166:24)
    at PluginContextImpl.emitFile (node_modules/rolldown/dist/shared/bindingify-input-options-XrReX_BJ.mjs:1353:82)
    at PluginContextImpl.generateBundle (node_modules/vite/dist/node/chunks/node.js:31808:10)

Validations