cloudflare / cloudflare/workers-sdk
[wrangler] Remote Hyperdrive bindings under getPlatformProxy() never re-seed edge credentials, so a stale dev session never recovers
- Dominant language
- TypeScript
- Stars
- 4.5k
- Forks
- 1.5k
- Avg merge
- 3d 8h
- Merged PRs (30d)
- 187
Description
### What versions & operating system are you using?
Wrangler 4.129.0 (built from `main` plus #14900, commit `29410697`), Node v26.4.0, macOS 26.5.2.
### Please provide a link to a minimal reproduction
No public repository — the reproduction needs a real Hyperdrive configuration on the account and several hours of dev-server idle time to surface. The setup is small enough to inline; see below.
### Describe the Bug
A `remote: true` Hyperdrive binding under `getPlatformProxy()` (e.g. `vite dev` via `@sveltejs/adapter-cloudflare`'s `platformProxy` option) works correctly for a while, then every connection attempt starts failing — and never recovers on its own. Only restarting the dev process fixes it.
**Reproduction.** A Worker with one or more `remote: true` Hyperdrive bindings and no `localConnectionString`, run under `vite dev` (or any other `getPlatformProxy()` consumer) for an extended period — in the sessions that surfaced this, failure appeared after roughly 5–8 hours of the process sitting idle (no requests, no file edits). Once it starts, every subsequent request to every Hyperdrive binding fails identically until the process is restarted.
**What is established.**
- The failure happens at connection-establishment time, not during query execution. Instrumenting the failure's full `cause` chain shows the throw originates from the Node driver's `createConnection(...)` call (not from a query already in flight), with a MySQL protocol-level error: `errno=1002 code=ER_NO message=Internal error.` This is consistent with the edge rejecting the connection attempt itself, not a query timing out.
- This is not a stale/reused socket: this driver setup opens a brand-new connection per request (no pooling), so a *new* connection attempt fails the same way an old one would have. Ruling that out points at whatever credential or session state is used to authenticate a fresh connection attempt, not at connection reuse.
- `seedRemoteHyperdriveBindings()` (in `packages/remote-bindings/src/seed-hyperdrive-bindings.ts`) fetches the edge session's per-session Hyperdrive credentials and is called exactly once, inside `getPlatformProxy()` (`packages/wrangler/src/api/integrations/platform/index.ts`). Its result is baked into the one Miniflare instance built for the life of the process. Nothing calls it again — not on a dev reload (which doesn't re-invoke `getPlatformProxy()`), and not on any kind of periodic or on-error basis.
- `maybeStartOrUpdateRemoteProxySession()` (`packages/remote-bindings/src/maybe-start-or-update-session.ts`) reuses the existing remote proxy session indefinitely as long as `auth` and the set of remote bindings haven't changed — it never checks whether the underlying session (or, in this case, the credentials seeded from it) is still valid.
Put together: if the edge-minted per-session Hyperdrive credentials have any expiry, there is no code path anywhere that would notice and re-seed them. The dev process keeps presenting the same now-invalid credentials to every new connection attempt, forever, until it is restarted and `getPlatformProxy()` runs again from scratch.
I have not verified whether this also affects other remote binding types that go through the same shared session-reuse layer (KV, R2, D1, etc.) — only Hyperdrive was reproduced and traced in this investigation.
### Please provide any relevant error logs
Full `cause` chain for one failed connection attempt, captured with a temporary diagnostic that walks `error.cause` recursively (not present in the shipped code):
```
[HD-DIAG] cause[0] name=Error message=Failed query: select version() as v
[HD-DIAG] cause[1] name=Error message=Internal error. code=ER_NO errno=1002
[HD-DIAG] cause[1] stack=Error: Internal error.
at eval (.../src/lib/server/db/driver/workers.ts:118:89)
at open (.../src/lib/server/db/driver/workers.ts:84:15)
at Object.query (.../src/lib/server/db/driver/workers.ts:92:29)
at .../drizzle-orm/mysql2/session.js:53:29
...
```
`open()` at that line is the driver's lazy-connect path (`createConnection({...}).then(...)`) — i.e. this is a fresh connection attempt failing, not a query on an existing one.
Contributor guide
Research direction
Start with seedRemoteHyperdriveBindings() in packages/remote-bindings/src/seed-hyperdrive-bindings.ts and its call from getPlatformProxy() in packages/wrangler/src/api/integrations/platform/index.ts. Then read maybeStartOrUpdateRemoteProxySession() in packages/remote-bindings/src/maybe-start-or-update-session.ts to trace session reuse and credential lifetime. Done means a long-lived dev process can recover remote Hyperdrive connections without restarting, while preserving the existing behavior for unchanged bindings.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100