Skip to content

[vite-plugin] vite build never exits when remoteBindings is enabled — remote proxy session is never disposed #15173

Description

@alexander-zuev

With remoteBindings enabled, vite build produces all output correctly and then hangs forever. The process never exits.

Environment

  • @cloudflare/vite-plugin@1.51.1
  • wrangler@4.120.0
  • vite@8.2.1
  • TanStack Start, prerendering enabled
  • Worker has 7 Durable Object bindings and D1/KV/VPC bindings with "remote": true

Reproduction

Run a build with remoteBindings: true in the plugin options and prerendering enabled.

The remote connection is established fine (no error 10375, so #13956 is genuinely fixed — thank you). The prerender pass completes:

⎔ Establishing remote connection...
[prerender] Prerendering pages...
[prerender] Crawling: /
GET / 200 OK (89ms)
...
[prerender] Prerendered 117 pages:

Every file is written to dist/, the output is correct, and then the process just sits there. It has to be killed manually.

Cause

startRemoteProxySession in wrangler creates a full DevEnv with a listening server:

const workerConfig = {
  name: options.workerName ?? crypto2.randomUUID(),
  entrypointSource: ProxyServerWorker_default,
  ...
  server: { port: 0, secure: false }
};
let devEnv;
try {
  devEnv = new DevEnv(workerConfig);
  devEnv.start();
}

That listening server is a referenced libuv handle, so Node's event loop cannot drain while it stays open.

On the plugin side, sessions are kept in a module-level map:

/** Map that maps worker configPaths to their existing remote proxy session data (if any) */
const remoteProxySessionsDataMap = new Map();

Grepping the built plugin (dist/index.mjs of 1.51.1), that map has 2 gets and 2 sets (one pair on the dev server path, one on the preview server path) and zero deletes. The only session.dispose() call in the whole bundle is:

if (!authSameAsBefore) {
  if (preExistingRemoteProxySessionData?.session)
    await preExistingRemoteProxySessionData.session.dispose();
  remoteProxySession = await startSession(remoteBindings, { ... });
}

It only fires to replace a session when auth changes. There is no teardown when the preview server closes or when the build ends.

For vite dev this is invisible, since the server runs until you interrupt it. For vite build, the preview server exists only to serve the prerender pass, so once the pass finishes nothing disposes the session and the build hangs.

Expected

The preview server's remote proxy session should be disposed when the build completes, so vite build exits normally.

Impact

Remote bindings cannot be used in any CI build. That rules out prerendering pages against real data, which is otherwise exactly what remote bindings make possible. In our case the prerendered output was perfect (real values from D1 dehydrated into the static HTML) and the only thing standing in the way is the process not exiting.

I could not find an existing issue for this, and the 1.52.0 changelog does not appear to touch session disposal or build exit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    package:vite-pluginRelating to the `@cloudflare/vite-plugin` package

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions