Schema Mismatch
Symptom
Section titled “Symptom”- Push / pull requests return HTTP 409 with body
{ "error": "schema mismatch", "expected": "..." } client.onErrorfires with{ kind: "schema-mismatch", phase, expected }- The Devtools panel shows a “schema mismatch” badge
What it means
Section titled “What it means”The SyncHandlerOptions.schemaVersion on the Worker doesn’t equal
the PlasmaClientOptions.schemaVersion on the browser client. The
server refuses to accept mutations under an unknown schema version.
Root causes
Section titled “Root causes”- You bumped
SCHEMA_VERSIONin the shared file but didn’t deploy the server. The client build hasv2; the deployed Worker still runsv1. - You deployed the server first. The Worker has
v2; the users’ cached client build still hasv1. - You have two
schema.tsfiles that drifted. The Worker’s copy and the client’s copy accidentally have different string constants.
Fix — server too old
Section titled “Fix — server too old”Deploy the Worker to catch up:
pnpm wrangler deployThen check the log stream that the new version is receiving the push:
pnpm wrangler tailFix — client too old
Section titled “Fix — client too old”If your users have a stale bundle cached, you need them to reload. The cleanest UX:
createPlasmaClient({ ..., onSchemaMismatch: async ({ phase }) => { if (confirm("This app has been updated. Reload to continue?")) { window.location.reload() return "stay" } return "stay" },})Returning "stay" leaves the local IDB alone; the user is stuck
until they reload. Alternatively return "reset" to wipe the local
IDB (see the trade-off below).
onSchemaMismatch return values
Section titled “onSchemaMismatch return values”| Return | Effect |
|---|---|
"stay" |
Local IDB untouched. Sync loop keeps failing until the mismatch is resolved. User can keep making local mutations (they land in outbox). |
"reset" |
Local IDB wiped: base stores, user stores, outbox, cookie, clientID all rotated. Next pull hydrates from scratch. |
Preventing recurrence
Section titled “Preventing recurrence”- Bump
SCHEMA_VERSIONdeliberately. See Migrations for what changes require a bump vs what changes don’t. - Deploy server before client. The server accepts older client schema versions silently (as long as no additive-required column is missing); a new client hitting an old server is the failure mode.
- Version your bundles. Cache-Control on the JS bundle should encourage prompt reloading on version bumps.
Debugging in the dev loop
Section titled “Debugging in the dev loop”Force a mismatch locally to test your onSchemaMismatch UX:
const plasma = createPlasmaClient({ ..., schemaVersion: "wrong-version", // ≠ what the Worker returns})What to read next
Section titled “What to read next”- Concepts / Push, Pull, Rebase — where the 409 fits in the sync loop
- Migrations — when to bump SCHEMA_VERSION