fix(bundled-dev): errors should be kept when incremental build fails#22617
Merged
Conversation
graphite-app Bot
pushed a commit
to rolldown/rolldown
that referenced
this pull request
Jun 11, 2026
…9652) ## Design Principles This PR mainly clarifies a few principles that we hold while implementing Rolldown's dev engine and Vite's full-bundle mode dev server. Mostly are written in `meta/design/dev-engine.md`. Just to give you a briefing, here're these things: 1. **Be conservative** — rebuild only when the bundle is stale, and defer it until the full bundle is actually requested (e.g. a page refresh). By default we ship HMR-only output. 2. **Replay errors when needed** — the dev *engine* is stateless across HTTP requests and never caches results from `onHmrUpdates`/`onOutput`; caching and replay are the dev *server*'s job, so errors survive a refresh. 3. **File changes are the only recovery trigger** — after a failed build the engine waits for a file edit (Vite config or source). Not a page refresh, elapsed time, or UI dismissal. 4. **Build errors are always recoverable** — every error via `on_output`/`on_hmr_updates` is treated as a user error (source/plugin), fixable by editing source. Only a panic is unrecoverable. ### Problem #9552 made `ensure_latest_bundle_output` return `None` in `FullBuildFailed` / `Failed` instead of retrying a build. That fixed a double-`onOutput` and an infinite loop — but the retry it removed had been doing double duty: on a browser refresh, the dev-server middleware calls `ensureLatestBuildOutput`, the retry reran the build, and `onOutput` re-fired the error to the client. With the retry gone, a refresh no longer reruns the build, so **a project whose build output contains an error stops re-emitting that error on refresh** — the error overlay disappears even though the build is still broken. The right fix is *not* to rerun the build on access (that brings back the loop and the double-emit). It's to make `Failed` / `FullBuildFailed` work as the genuine resting states they now are: - the error must survive a refresh **without** a rebuild, and - recovery must be driven entirely by file changes, re-running the stage that actually broke. ### Changes **1. Expose errored-and-stale state to the consumer.** Snapshot field `last_full_build_failed` → `last_build_errored`, now true for *both* `FullBuildFailed` and `Failed { .. }`. This is the predicate the consumer pairs with `has_stale_output` to (a) replay its cached build error on client reconnect instead of triggering a doomed regen, and (b) avoid the access-triggered reload loop against a bundle the engine won't regenerate. **2. File-change-driven, stage-aware recovery.** Because access no longer retries, the next file change is the only recovery trigger — and it must re-run the broken stage: - New `ErrorStage` enum (`Hmr` | `Rebuild`); `BundlingTask` tracks `hmr_errored` / `rebuild_errored` and collapses them with precedence `Rebuild > Hmr` (covers the `Hmr → HmrRebuild` auto-upgrade that then fails in rebuild). - `CoordinatorState::Failed { last_error_stage }` carries the stage; `handle_file_changes` forces `HmrRebuild` on a `Rebuild`-stage failure (regardless of `rebuild_strategy`) so the rebuild re-runs, and uses an `Hmr` task for an `Hmr`-stage failure. ### Tests Tests will be added when Rolldown's test-dev-server is fully aligned with Vite's full-bundle mode server. This PR is meant to be released along with vitejs/vite#22617.
e936374 to
41fead5
Compare
a1e3b31 to
9cd097e
Compare
sapphi-red
approved these changes
Jun 23, 2026
renovate Bot
added a commit
to andrei-picus-tink/auto-renovate
that referenced
this pull request
Jun 24, 2026
| datasource | package | from | to | | ---------- | ------- | ------ | ----- | | npm | vite | 8.0.14 | 8.1.0 | ## [v8.1.0](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#810-2026-06-23) ##### Features - extend `server.fs.deny` list with common files ([#22707](vitejs/vite#22707)) ([61ba8fd](vitejs/vite@61ba8fd)) - update rolldown to 1.1.2 ([#22695](vitejs/vite#22695)) ([4f008a6](vitejs/vite@4f008a6)) - use `~` for Rolldown ([#22693](vitejs/vite#22693)) ([9928722](vitejs/vite@9928722)) ##### Bug Fixes - **bundled-dev:** errors should be kept when incremental build fails ([#22617](vitejs/vite#22617)) ([9a0dd48](vitejs/vite@9a0dd48)) - cache falsy values in perEnvironmentState ([#22715](vitejs/vite#22715)) ([0e91e79](vitejs/vite@0e91e79)) - **glob:** respect caseSensitive option in hmr matcher ([#22711](vitejs/vite#22711)) ([65f525e](vitejs/vite@65f525e)) - **html:** omit nonce on import map when cspNonce is unset ([#22713](vitejs/vite#22713)) ([8340bb5](vitejs/vite@8340bb5)) - **optimizer:** skip null-valued exports in expandGlobIds glob resolution ([#22611](vitejs/vite#22611)) ([8b9f5cd](vitejs/vite@8b9f5cd)) - resolved build options should be kept as a getter ([#22691](vitejs/vite#22691)) ([3527191](vitejs/vite@3527191)) - **server:** handle malformed URI in memory files middleware ([#22714](vitejs/vite#22714)) ([df9e0a5](vitejs/vite@df9e0a5)) - use literal envPrefix queries for Vite Task ([#22706](vitejs/vite#22706)) ([da72733](vitejs/vite@da72733)) - warn on deprecated envFile ([#22555](vitejs/vite#22555)) ([ed7b283](vitejs/vite@ed7b283)) ##### Code Refactoring - **client:** inline dev-id value in CSS selector ([#22736](vitejs/vite#22736)) ([57f59bc](vitejs/vite@57f59bc)) - remove unused removeRawQuery util ([#22724](vitejs/vite#22724)) ([403cc60](vitejs/vite@403cc60)) - use `rolldownOptions` property for chunkImportMap ([#22692](vitejs/vite#22692)) ([8e8816c](vitejs/vite@8e8816c)) ## [v8.0.16](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-8016-2026-06-01-small) ##### Bug Fixes - **deps:** reject UNC paths for launch-editor-middleware ([#22571](vitejs/vite#22571)) ([50b9512](vitejs/vite@50b9512)) - reject windows alternate paths ([#22572](vitejs/vite#22572)) ([dc245c7](vitejs/vite@dc245c7)) ## [v8.0.15](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-8015-2026-06-01-small) ##### Features - send 408 on request timeout ([#22476](vitejs/vite#22476)) ([c85c9ee](vitejs/vite@c85c9ee)) - update rolldown to 1.0.3 ([#22538](vitejs/vite#22538)) ([646dbed](vitejs/vite@646dbed)) ##### Bug Fixes - capitalize error messages and remove spurious space in parse error ([#22488](vitejs/vite#22488)) ([85a0eff](vitejs/vite@85a0eff)) - **deps:** update all non-major dependencies ([#22511](vitejs/vite#22511)) ([2686d7d](vitejs/vite@2686d7d)) - **dev:** fix html-proxy cache key mismatch for /@fs/ HTML paths ([#21762](vitejs/vite#21762)) ([47c4213](vitejs/vite@47c4213)) - **glob:** error on relative glob in virtual module when no files match ([#22497](vitejs/vite#22497)) ([5c8e98f](vitejs/vite@5c8e98f)) - **optimizer:** close the rolldown bundle when write() rejects ([#22528](vitejs/vite#22528)) ([e3cfb9d](vitejs/vite@e3cfb9d)) - **resolve:** provide onWarn for viteResolvePlugin in JS plugin containers ([#22509](vitejs/vite#22509)) ([40985f1](vitejs/vite@40985f1)) ##### Miscellaneous Chores - **deps:** update rolldown-related dependencies ([#22566](vitejs/vite#22566)) ([3052a67](vitejs/vite@3052a67)) ##### Code Refactoring - correct logic in `collectAllModules` function ([#22562](vitejs/vite#22562)) ([6978a9c](vitejs/vite@6978a9c))
renovate Bot
added a commit
to andrei-picus-tink/auto-renovate
that referenced
this pull request
Jun 24, 2026
| datasource | package | from | to | | ---------- | ------- | ------ | ----- | | npm | vite | 8.0.14 | 8.1.0 | ## [v8.1.0](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#810-2026-06-23) ##### Features - extend `server.fs.deny` list with common files ([#22707](vitejs/vite#22707)) ([61ba8fd](vitejs/vite@61ba8fd)) - update rolldown to 1.1.2 ([#22695](vitejs/vite#22695)) ([4f008a6](vitejs/vite@4f008a6)) - use `~` for Rolldown ([#22693](vitejs/vite#22693)) ([9928722](vitejs/vite@9928722)) ##### Bug Fixes - **bundled-dev:** errors should be kept when incremental build fails ([#22617](vitejs/vite#22617)) ([9a0dd48](vitejs/vite@9a0dd48)) - cache falsy values in perEnvironmentState ([#22715](vitejs/vite#22715)) ([0e91e79](vitejs/vite@0e91e79)) - **glob:** respect caseSensitive option in hmr matcher ([#22711](vitejs/vite#22711)) ([65f525e](vitejs/vite@65f525e)) - **html:** omit nonce on import map when cspNonce is unset ([#22713](vitejs/vite#22713)) ([8340bb5](vitejs/vite@8340bb5)) - **optimizer:** skip null-valued exports in expandGlobIds glob resolution ([#22611](vitejs/vite#22611)) ([8b9f5cd](vitejs/vite@8b9f5cd)) - resolved build options should be kept as a getter ([#22691](vitejs/vite#22691)) ([3527191](vitejs/vite@3527191)) - **server:** handle malformed URI in memory files middleware ([#22714](vitejs/vite#22714)) ([df9e0a5](vitejs/vite@df9e0a5)) - use literal envPrefix queries for Vite Task ([#22706](vitejs/vite#22706)) ([da72733](vitejs/vite@da72733)) - warn on deprecated envFile ([#22555](vitejs/vite#22555)) ([ed7b283](vitejs/vite@ed7b283)) ##### Code Refactoring - **client:** inline dev-id value in CSS selector ([#22736](vitejs/vite#22736)) ([57f59bc](vitejs/vite@57f59bc)) - remove unused removeRawQuery util ([#22724](vitejs/vite#22724)) ([403cc60](vitejs/vite@403cc60)) - use `rolldownOptions` property for chunkImportMap ([#22692](vitejs/vite#22692)) ([8e8816c](vitejs/vite@8e8816c)) ## [v8.0.16](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-8016-2026-06-01-small) ##### Bug Fixes - **deps:** reject UNC paths for launch-editor-middleware ([#22571](vitejs/vite#22571)) ([50b9512](vitejs/vite@50b9512)) - reject windows alternate paths ([#22572](vitejs/vite#22572)) ([dc245c7](vitejs/vite@dc245c7)) ## [v8.0.15](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-8015-2026-06-01-small) ##### Features - send 408 on request timeout ([#22476](vitejs/vite#22476)) ([c85c9ee](vitejs/vite@c85c9ee)) - update rolldown to 1.0.3 ([#22538](vitejs/vite#22538)) ([646dbed](vitejs/vite@646dbed)) ##### Bug Fixes - capitalize error messages and remove spurious space in parse error ([#22488](vitejs/vite#22488)) ([85a0eff](vitejs/vite@85a0eff)) - **deps:** update all non-major dependencies ([#22511](vitejs/vite#22511)) ([2686d7d](vitejs/vite@2686d7d)) - **dev:** fix html-proxy cache key mismatch for /@fs/ HTML paths ([#21762](vitejs/vite#21762)) ([47c4213](vitejs/vite@47c4213)) - **glob:** error on relative glob in virtual module when no files match ([#22497](vitejs/vite#22497)) ([5c8e98f](vitejs/vite@5c8e98f)) - **optimizer:** close the rolldown bundle when write() rejects ([#22528](vitejs/vite#22528)) ([e3cfb9d](vitejs/vite@e3cfb9d)) - **resolve:** provide onWarn for viteResolvePlugin in JS plugin containers ([#22509](vitejs/vite#22509)) ([40985f1](vitejs/vite@40985f1)) ##### Miscellaneous Chores - **deps:** update rolldown-related dependencies ([#22566](vitejs/vite#22566)) ([3052a67](vitejs/vite@3052a67)) ##### Code Refactoring - correct logic in `collectAllModules` function ([#22562](vitejs/vite#22562)) ([6978a9c](vitejs/vite@6978a9c))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR fixed a case where errors are not kept after page refreshes when incremental build fails.
For details, please refer to rolldown/rolldown#9652. In short, Rolldown previously retried every failed builds whether it's full build or it's an incremental rebuild. This always printed the error. After Rolldown's PR rolldown/rolldown#9552, this retry logic had gone. This means that the errors are not getting emitted, causing errors are not being able to get printed again.
In Rolldown's fix rolldown/rolldown#9652, based on the principles discussed within the team, we came to an agreement that we hold a very conservative stance on re-bundling. Thus, in this PR, Vite, as a consumer of Rolldown's dev engine, it caches the error and only rebuilds when it's necessary. This PR basically adapts the current dev server with Rolldown's new implementation.
For more details on the fix, refer to Rolldown's fix: rolldown/rolldown#9652
Vite test harness and dev server are in sync with Rolldown.