Skip to content

wrangler dev --local crashes the whole process on a recoverable D1 SQLITE_BUSY under concurrent writes (miniflare 4.20260722; regression from 4.20260714) #14916

Description

@msnelling

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).

  1. wrangler dev --local --persist-to <dir> with one or more service-bound workers that write to a shared local D1.
  2. Drive enough concurrent write traffic that miniflare's SQLite returns SQLITE_BUSY / SQLITE_BUSY_SNAPSHOT.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    package:miniflareRelating to Miniflareproduct:d1Relating to Cloudflare D1: https://developers.cloudflare.com/d1/regressionBreak in existing functionality as a result of a recent change

    Type

    Projects

    Status
    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions