Which Cloudflare product(s) does this pertain to?
Wrangler core, Miniflare (local D1)
Wrangler / Miniflare version
- Affected:
wrangler@4.114.0 (miniflare 4.20260722.0, workerd 1.20260722.1) and wrangler@4.115.0 (miniflare 4.20260722.1)
- Not affected:
wrangler@4.112.0 (miniflare 4.20260714.0, workerd 1.20260714.1)
Description
Under wrangler dev --local, a recoverable local D1 SQLITE_BUSY (database is locked) crashes the entire wrangler dev process instead of surfacing to the Worker as a retryable error.
The D1 queryExecute failure is raised as a workerd jsgInternalError, propagates out of miniflare's loopback custom-fetch handler, and ProxyController.emitErrorEvent turns it into an uncaught error that exits the dev server. Because a single wrangler dev invocation hosts several service-bound workers (-c multi-config), the crash takes all workers down at once — every subsequent request gets ECONNREFUSED.
This is a regression: on wrangler@4.112.0 (miniflare 4.20260714.0) the same SQLITE_BUSY is retried and the dev server stays up (you can see the internal "Retrying D1 … due to transient error" path fire). It first breaks at 4.114.0 (miniflare 4.20260722.0) and still reproduces on 4.115.0.
Steps to reproduce
Trigger a local D1 SQLITE_BUSY by issuing concurrent writes against the same local D1 database — e.g. several parallel requests that each INSERT/UPDATE (our repro is an E2E suite running 4 parallel browser workers that concurrently create orgs + users through a Worker backed by local D1).
wrangler dev --local --persist-to <dir> with one or more service-bound workers that write to a shared local D1.
- Drive enough concurrent write traffic that miniflare's SQLite returns
SQLITE_BUSY / SQLITE_BUSY_SNAPSHOT.
- On
wrangler@4.114.0/4.115.0 the dev process exits at the first such error; on 4.112.0 it retries and stays up.
(Happy to build a minimal standalone repro if the signature below isn't enough to locate it.)
Relevant log / crash signature
✘ [ERROR] e = workerd/util/sqlite.c++:1671: failed: SENTRY_DO SQLite failed;
NOSENTRY database is locked: SQLITE_BUSY (extended: SQLITE_BUSY_SNAPSHOT)
sentryErrorContext = jsgInternalError; wdErrId = <id>
[wrangler:error] Error: internal error; reference = <id>
at D1DatabaseObject.queryExecute (miniflare/src/workers/d1/database.worker.ts:228:31)
at async D1DatabaseObject.fetch (miniflare/src/workers/shared/router.worker.ts:45:32)
Error in ProxyController: Error inside ProxyWorker
at castErrorCause (wrangler-dist/cli.js)
at ProxyController2.emitErrorEvent (wrangler-dist/cli.js)
at ProxyController2.onProxyWorkerMessage (wrangler-dist/cli.js)
at #handleLoopbackCustomFetchService (miniflare/dist/src/index.js)
at #handleLoopback (miniflare/dist/src/index.js)
"event":"wrangler command errored","wranglerVersion":"4.114.0","sanitizedCommand":"dev"
# -> wrangler dev process exits; all workers now ECONNREFUSED
Expected behaviour
A local D1 SQLITE_BUSY should be retried internally and/or surfaced to the Worker as a normal query error — it must not crash the wrangler dev process. (This is how 4.112.0 / miniflare 4.20260714.0 behaves.)
Actual behaviour
The error escapes miniflare's loopback handler, ProxyController.emitErrorEvent rethrows, and the whole wrangler dev process exits, taking every hosted worker offline.
Environment
- OS: Linux (Ubuntu), Node 26, package manager: bun
wrangler dev --local --persist-to <dir> with multiple -c service configs (router + service-bound workers), D1 local backing store
- Intermittent — depends on a
SQLITE_BUSY landing under concurrent writes
Bisect
Regressed between wrangler@4.112.0 (miniflare 4.20260714.0) — good — and wrangler@4.114.0 (miniflare 4.20260722.0) — bad. 4.115.0 (miniflare 4.20260722.1) still affected. Current workaround: pin wrangler@4.112.0.
Which Cloudflare product(s) does this pertain to?
Wrangler core, Miniflare (local D1)
Wrangler / Miniflare version
wrangler@4.114.0(miniflare4.20260722.0, workerd1.20260722.1) andwrangler@4.115.0(miniflare4.20260722.1)wrangler@4.112.0(miniflare4.20260714.0, workerd1.20260714.1)Description
Under
wrangler dev --local, a recoverable local D1SQLITE_BUSY(database is locked) crashes the entirewrangler devprocess instead of surfacing to the Worker as a retryable error.The D1
queryExecutefailure is raised as a workerdjsgInternalError, propagates out of miniflare's loopback custom-fetch handler, andProxyController.emitErrorEventturns it into an uncaught error that exits the dev server. Because a singlewrangler devinvocation hosts several service-bound workers (-cmulti-config), the crash takes all workers down at once — every subsequent request getsECONNREFUSED.This is a regression: on
wrangler@4.112.0(miniflare4.20260714.0) the sameSQLITE_BUSYis retried and the dev server stays up (you can see the internal "Retrying D1 … due to transient error" path fire). It first breaks at4.114.0(miniflare4.20260722.0) and still reproduces on4.115.0.Steps to reproduce
Trigger a local D1
SQLITE_BUSYby issuing concurrent writes against the same local D1 database — e.g. several parallel requests that eachINSERT/UPDATE(our repro is an E2E suite running 4 parallel browser workers that concurrently create orgs + users through a Worker backed by local D1).wrangler dev --local --persist-to <dir>with one or more service-bound workers that write to a shared local D1.SQLITE_BUSY/SQLITE_BUSY_SNAPSHOT.wrangler@4.114.0/4.115.0the dev process exits at the first such error; on4.112.0it retries and stays up.(Happy to build a minimal standalone repro if the signature below isn't enough to locate it.)
Relevant log / crash signature
Expected behaviour
A local D1
SQLITE_BUSYshould be retried internally and/or surfaced to the Worker as a normal query error — it must not crash thewrangler devprocess. (This is how4.112.0/ miniflare4.20260714.0behaves.)Actual behaviour
The error escapes miniflare's loopback handler,
ProxyController.emitErrorEventrethrows, and the wholewrangler devprocess exits, taking every hosted worker offline.Environment
wrangler dev --local --persist-to <dir>with multiple-cservice configs (router + service-bound workers), D1 local backing storeSQLITE_BUSYlanding under concurrent writesBisect
Regressed between
wrangler@4.112.0(miniflare4.20260714.0) — good — andwrangler@4.114.0(miniflare4.20260722.0) — bad.4.115.0(miniflare4.20260722.1) still affected. Current workaround: pinwrangler@4.112.0.