cloudflare / cloudflare/workers-sdk

D1 binding reads stale schema despite correct on-disk data (vite dev, single entry worker)

Open
#15,362 1 comment 0 reactions 0 assignees View on GitHub
product:d1
Dominant language
TypeScript
Stars
4.5k
Forks
1.5k
Avg merge
3d 8h
Merged PRs (30d)
186

Description

Using \`@cloudflare/vite-plugin\` with a single entry worker (no auxiliaryWorkers) that has a D1 binding. After running \`wrangler d1 migrations apply --local\` and seeding data, app code calling \`env.DB.prepare(...)\` throws \`no such table\` / \`no such column\` for schema added in recent migrations — even though the same database is verifiably up to date:

- The on-disk sqlite file (resolved via an explicit \`persistState.path\`) has the correct schema when queried directly with \`sqlite3\`.
- A raw query through the plugin's own Local Explorer API (\`/cdn-cgi/local/explorer/api/d1/database/{id}/raw\`) against the same \`database_id\` returns correct, current data.

Only the binding used by app code (\`platform.env.DB\` in a SvelteKit route, presumably going through D1's Sessions API) sees the stale schema. This reproduces from a completely fresh state — wipe \`.wrangler/state\`, re-run migrations + seed, start \`vite dev\` for the first time — so it isn't a race or leftover cache from a previous run. Error is thrown from \`D1DatabaseSessionAlwaysPrimary._sendOrThrow\`.

**Repro**
1. \`wrangler.toml\` with a D1 binding, \`main\` pointing at an adapter build output (SvelteKit + \`adapter-cloudflare\` in our case).
2. \`wrangler d1 migrations apply --local -c wrangler.toml\`, then seed a row that uses a column/table from a later migration.
3. \`cloudflare()\` in \`vite.config.ts\` plugins, with an explicit \`persistState: { path: '...' }\` pointing at the same directory wrangler used.
4. \`vite dev\`, hit a route that queries the new column/table via the D1 binding → \`SQLITE_ERROR\`.
5. Query the same \`database_id\` via the Local Explorer raw-query route → correct data, same file.

**Versions**: \`@cloudflare/vite-plugin\` 1.54.0, \`wrangler\` 4.126.0, \`vite\` 7.3.2, Node 26.7.0, macOS.

Possibly related but doesn't look like a full duplicate: #9511 and #8980 — both are about \`persistState\` defaults / D1 access through a service binding. This reproduces with a single entry worker and \`persistState.path\` already explicit, so the fix for #9511 doesn't seem to cover it.

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue from a clean .wrangler/state using wrangler.toml, vite.config.ts, the explicit persistState.path, and the described SvelteKit route. Compare the binding path through D1DatabaseSessionAlwaysPrimary._sendOrThrow with the Local Explorer raw-query endpoint and direct sqlite3 results. Done means the app binding reads the current schema and data after local migrations and seeding.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript, vite
Domain
databases, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.