cloudflare / cloudflare/workers-sdk
D1 binding reads stale schema despite correct on-disk data (vite dev, single entry worker)
- 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
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