Describe the bug
In Vite's experimental full-bundle dev mode (experimental.bundledDev: true), importing a .css file in a project that also uses PostCSS causes a hard build-time crash. The dev server starts, but as soon as the CSS module is pulled in through Rolldown's lazy bundling, the build fails and never recovers. The browser is stuck on the bundledDev "Bundling in progress" screen with the error overlay, and no page reload recovers — the bundle itself fails to build.
What I expect: the CSS module is processed by PostCSS as CSS and the page renders.
What actually happens: vite:css runs PostCSS on a JavaScript proxy module and PostCSS throws while trying to parse JS as CSS.
✘ Build error: Build failed with 1 error:
[plugin vite:css] /path/to/src/global.css?rolldown-lazy=1:15:41
CssSyntaxError: [postcss] /path/to/src/global.css?rolldown-lazy=1:15:42: Unclosed string
at Input.error (.../node_modules/postcss/lib/input.js:135:16)
at unclosed (.../node_modules/postcss/lib/tokenize.js:46:17)
at Object.nextToken (.../node_modules/postcss/lib/tokenize.js:161:15)
at Parser.other (.../node_modules/postcss/lib/parser.js:428:30)
at Parser.parse (.../node_modules/postcss/lib/parser.js:479:16)
at parse (.../node_modules/postcss/lib/parse.js:11:12)
at new LazyResult (.../node_modules/postcss/lib/lazy-result.js:165:16)
at Processor.process (.../node_modules/postcss/lib/processor.js:53:14)
at runPostCSS (.../node_modules/vite/dist/node/chunks/node.js:21166:66)
at async compilePostCSS (.../node_modules/vite/dist/node/chunks/node.js:21150:6)
Root cause
In bundledDev mode, Rolldown's lazy bundling wraps each lazily-loaded module in a JavaScript proxy. For a CSS module it keeps the original id and appends a query, …/src/global.css?rolldown-lazy=1, but the content of that module is JavaScript, roughly:
const lazyExports = (async () => {
delete __rolldown_runtime__.modules["src/global.css?rolldown-lazy=1"];
await import(/* @vite-ignore */ `/@vite/lazy?id=${encodeURIComponent(...)}&clientId=...`);
return __rolldown_runtime__.loadExports("src/global.css");
})();
export { lazyExports as 'rolldown:exports' };
Vite's vite:css plugin decides whether to transform a module by matching the file extension:
const CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/
and only skips ids matching commonjsProxyRE and SPECIAL_QUERY_RE (/[?&](?:worker|sharedworker|raw|url)\b/). The ?rolldown-lazy query is not excluded, so vite:css runs PostCSS (runPostCSS / compilePostCSS) on the JavaScript proxy module. PostCSS tries to parse JS as CSS and throws Unclosed string (the quoted export name in export { lazyExports as '…' }).
Suggested fix
vite:css's transform should skip ids that carry the ?rolldown-lazy query — those are Rolldown JS proxies, not CSS (e.g. add it to the special-query exclusion or short-circuit when the query is present).
I have not submitted a PR for this.
Reproduction
https://github.com/happycollision/vite8-bundleddev-postcss-repro
The repository is the smallest project that reproduces it: a vanilla Vite app with experimental.bundledDev: true, a postcss.config.js registering autoprefixer, an index.html, an entry src/main.js that imports a .css file, and a valid src/global.css.
Steps to reproduce
git clone https://github.com/happycollision/vite8-bundleddev-postcss-repro
cd vite8-bundleddev-postcss-repro
npm install
npm run dev
# open http://localhost:5173/ in a browser
The dev server terminal prints the CssSyntaxError: Unclosed string build error shown above, and the browser is stuck on the "Bundling in progress" overlay. This happens with the dev server (npm run dev); vite build is not affected.
Details that matter for reproduction:
- PostCSS must actually run. The crash is inside
runPostCSS/compilePostCSS, so a real PostCSS plugin (autoprefixer) is configured. The sample CSS uses user-select, which autoprefixer rewrites — confirming PostCSS is in the pipeline, not no-op'd.
- The CSS is loaded through lazy bundling. In the repro
src/main.js uses await import('./global.css'), which forces Rolldown to emit the ?rolldown-lazy=1 proxy. (A statically-imported CSS module that lands in the initial entry bundle is inlined eagerly and does not hit this path.)
- The
.css itself is perfectly valid — this is not a CSS authoring error.
Regression range (bisected)
| Version |
Result |
| 8.0.0 / 8.0.8 / 8.0.12 |
✅ works |
| 8.0.13 |
❌ first broken |
| 8.0.14 / 8.0.16 |
❌ broken |
Verified in the repro: switching to [email protected] renders the app and processes the CSS with no error; switching back to [email protected] crashes with the trace above. Introduced in 8.0.13 by "bundled-dev: add lazy bundling support" (#21406).
Related issues
This is distinct from the two existing bundledDev reports:
This issue is different in kind: it is a hard build-time PostCSS crash (vite:css running PostCSS on a ?rolldown-lazy=1 JS proxy), not a recoverable first-load runtime failure. The bundle never builds, so a reload does not fix it.
System Info
System:
OS: macOS 26.5.1
CPU: (16) arm64 Apple M4 Max
Memory: 1.22 GB / 64.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.14.1
npm: 11.11.0
pnpm: 10.33.0
Browsers:
Chrome: 149.0.7827.54
Firefox: 151.0.2
Safari: 26.5
npmPackages:
vite: 8.0.16 => 8.0.16
rolldown: 1.0.3 (bundled via [email protected])
Used Package Manager
npm
Logs
Click to expand!
✘ Build error: Build failed with 1 error:
[plugin vite:css] /path/to/src/global.css?rolldown-lazy=1:15:41
CssSyntaxError: [postcss] /path/to/src/global.css?rolldown-lazy=1:15:42: Unclosed string
at Input.error (.../node_modules/postcss/lib/input.js:135:16)
at unclosed (.../node_modules/postcss/lib/tokenize.js:46:17)
at Object.nextToken (.../node_modules/postcss/lib/tokenize.js:161:15)
at Parser.other (.../node_modules/postcss/lib/parser.js:428:30)
at Parser.parse (.../node_modules/postcss/lib/parser.js:479:16)
at parse (.../node_modules/postcss/lib/parse.js:11:12)
at new LazyResult (.../node_modules/postcss/lib/lazy-result.js:165:16)
at Processor.process (.../node_modules/postcss/lib/processor.js:53:14)
at runPostCSS (.../node_modules/vite/dist/node/chunks/node.js:21166:66)
at async compilePostCSS (.../node_modules/vite/dist/node/chunks/node.js:21150:6)
The full captured output is committed in the reproduction as error-8.0.16.log.
Validations
Describe the bug
In Vite's experimental full-bundle dev mode (
experimental.bundledDev: true), importing a.cssfile in a project that also uses PostCSS causes a hard build-time crash. The dev server starts, but as soon as the CSS module is pulled in through Rolldown's lazy bundling, the build fails and never recovers. The browser is stuck on the bundledDev "Bundling in progress" screen with the error overlay, and no page reload recovers — the bundle itself fails to build.What I expect: the CSS module is processed by PostCSS as CSS and the page renders.
What actually happens:
vite:cssruns PostCSS on a JavaScript proxy module and PostCSS throws while trying to parse JS as CSS.Root cause
In
bundledDevmode, Rolldown's lazy bundling wraps each lazily-loaded module in a JavaScript proxy. For a CSS module it keeps the original id and appends a query,…/src/global.css?rolldown-lazy=1, but the content of that module is JavaScript, roughly:Vite's
vite:cssplugin decides whether to transform a module by matching the file extension:and only skips ids matching
commonjsProxyREandSPECIAL_QUERY_RE(/[?&](?:worker|sharedworker|raw|url)\b/). The?rolldown-lazyquery is not excluded, sovite:cssruns PostCSS (runPostCSS/compilePostCSS) on the JavaScript proxy module. PostCSS tries to parse JS as CSS and throwsUnclosed string(the quoted export name inexport { lazyExports as '…' }).Suggested fix
vite:css'stransformshould skip ids that carry the?rolldown-lazyquery — those are Rolldown JS proxies, not CSS (e.g. add it to the special-query exclusion or short-circuit when the query is present).I have not submitted a PR for this.
Reproduction
https://github.com/happycollision/vite8-bundleddev-postcss-repro
The repository is the smallest project that reproduces it: a vanilla Vite app with
experimental.bundledDev: true, apostcss.config.jsregisteringautoprefixer, anindex.html, an entrysrc/main.jsthat imports a.cssfile, and a validsrc/global.css.Steps to reproduce
The dev server terminal prints the
CssSyntaxError: Unclosed stringbuild error shown above, and the browser is stuck on the "Bundling in progress" overlay. This happens with the dev server (npm run dev);vite buildis not affected.Details that matter for reproduction:
runPostCSS/compilePostCSS, so a real PostCSS plugin (autoprefixer) is configured. The sample CSS usesuser-select, which autoprefixer rewrites — confirming PostCSS is in the pipeline, not no-op'd.src/main.jsusesawait import('./global.css'), which forces Rolldown to emit the?rolldown-lazy=1proxy. (A statically-imported CSS module that lands in the initial entry bundle is inlined eagerly and does not hit this path.).cssitself is perfectly valid — this is not a CSS authoring error.Regression range (bisected)
Verified in the repro: switching to
[email protected]renders the app and processes the CSS with no error; switching back to[email protected]crashes with the trace above. Introduced in 8.0.13 by "bundled-dev: add lazy bundling support" (#21406).Related issues
This is distinct from the two existing bundledDev reports:
bundledDev:trueand react lazy loading withresolve.alias#22454 (closed — "fixed in 8.0.14"): an alias × dynamic-import edge case. Different trigger, and that fix does not address this — our case is still broken in 8.0.16.bundledDev: true#22596 (open): a first-load asset issue, i.e. a recoverable runtime/first-load failure.This issue is different in kind: it is a hard build-time PostCSS crash (
vite:cssrunning PostCSS on a?rolldown-lazy=1JS proxy), not a recoverable first-load runtime failure. The bundle never builds, so a reload does not fix it.System Info
System: OS: macOS 26.5.1 CPU: (16) arm64 Apple M4 Max Memory: 1.22 GB / 64.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 24.14.1 npm: 11.11.0 pnpm: 10.33.0 Browsers: Chrome: 149.0.7827.54 Firefox: 151.0.2 Safari: 26.5 npmPackages: vite: 8.0.16 => 8.0.16 rolldown: 1.0.3 (bundled via [email protected])Used Package Manager
npm
Logs
Click to expand!
The full captured output is committed in the reproduction as
error-8.0.16.log.Validations