Skip to content

fix(vite-plugin): keep watching config changes after a failed restart#14610

Open
martijnwalraven wants to merge 2 commits into
cloudflare:mainfrom
martijnwalraven:callboard/vite-plugin-config-watch-rearm
Open

fix(vite-plugin): keep watching config changes after a failed restart#14610
martijnwalraven wants to merge 2 commits into
cloudflare:mainfrom
martijnwalraven:callboard/vite-plugin-config-watch-rearm

Conversation

@martijnwalraven

@martijnwalraven martijnwalraven commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Fixes a dev-server bug where the plugin permanently stops watching config changes after one failed restart.

The bug. configureServer in plugins/config.ts registers a change handler covering the Worker config files, local dev vars (.dev.vars/.env), and the assets configuration. It removed itself from the watcher (watcher.off) before calling viteDevServer.restart(). When the restart fails — e.g. the updated wrangler.json is invalid — Vite's restartServer catches the replacement server's creation failure internally, logs server restart failed, and resolves with the current server (and its watcher) still in place; only a successfully created server runs configureServer again, so nothing re-registers the handler. From that point on, every config change — including the one that fixes the config — is silently ignored. Source HMR keeps working, which makes the session look healthy while the server keeps serving a config that no longer matches the file. The behavior is the same on Vite 6, 7, and 8.

Repro (before this change): start vite dev, edit wrangler.json to something invalid (or a main that doesn't resolve), then fix it → the fix is never applied; only a manual dev-server restart recovers.

The fix. The handler stays registered for its server's whole life and guards against re-entrant restarts with an in-flight flag instead of unregistering. The guard silences only this server's handler while its own restart is in flight — the same window the previous code silenced by unregistering (a change landing mid-restart can still be picked up by the replacement server's handler once it is registered, as before). After a failed restart the next change re-attempts; after a successful restart the old watcher is closed and the new server has registered its own handler, so the stale handler never double-fires — the same lifecycle the export-shape restart in dev.ts already relies on.

The test extends the config-changes playground with the exact sequence: break the config (main → missing file), await the reported error, fix the config, assert the new var value is served. On the previous code it fails — notably at the first step already when running after the existing "reports errors in updates" test, whose failed restart has already killed config watching.


  • Tests
    • Tests included/updated (regression test in the config-changes playground; fails before the fix, passes after)
  • Public documentation
    • Documentation not necessary because: bug fix restoring the intended config-watching behavior in dev; no API change. A changeset is included.

🤖 Generated with Claude Code


Open in Devin Review

The config-change handler — which watches the Worker config files,
local dev vars (.dev.vars/.env), and the assets configuration —
unregistered itself from the watcher before calling
viteDevServer.restart(). When the restart fails (an invalid updated
config makes Vite's restartServer catch the replacement server's
creation failure, log 'server restart failed', and resolve with the
current server and its watcher left in place; the same behavior on
Vite 6, 7, and 8), nothing ever re-registered the handler: only a
successfully created server runs configureServer again. From that
point every config change, including the one fixing the config, was
silently ignored while source HMR kept working — the session looked
healthy while serving a config that no longer matched the file.

The handler now stays registered for its server's whole life and
guards against re-entrant restarts with an in-flight flag instead.
The guard silences only this server's handler while its own restart
is in flight — the same window the previous code silenced by
unregistering; a change landing in that window can still be picked up
by the replacement server's own handler once it is registered, as
before. After a failed restart the next change re-attempts. On a
successful restart the old watcher is closed and the new server
registers its own handler, so the stale handler never double-fires —
the same lifecycle the export-shape restart in dev.ts already relies
on.

The regression test drives the exact sequence through the
config-changes playground: break the config (main pointing at a
missing file), await the reported error, fix the config, and assert
the new var value is served. It fails on the previous code — at the
first step already when it runs after the existing broken-config
test, whose failed restart has permanently killed config watching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8d84aca

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Jul 8, 2026
@workers-devprod workers-devprod requested review from a team and dario-piotrowicz and removed request for a team July 8, 2026 19:37
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/config-watch-failed-restart.md: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/config-changes/tests/config-changes.spec.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/plugins/config.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@14610

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14610

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14610

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14610

miniflare

npm i https://pkg.pr.new/miniflare@14610

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14610

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14610

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14610

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14610

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14610

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14610

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14610

wrangler

npm i https://pkg.pr.new/wrangler@14610

commit: 8d84aca

REVIEW.md: changeset titles are plain descriptions, no conventional
commit prefixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants