Skip to content

Cloudflare Deploy

Symptom:

Error: D1_ERROR: database is locked at line 1: ...

Cause: wrangler dev is running against the same local D1 file and holds an exclusive lock. wrangler d1 execute can’t get a write handle while another process has it.

Fix:

  1. Stop wrangler dev first, run wrangler d1 execute, then start wrangler dev again.
  2. Or use wrangler dev --persist-to ./.wrangler/dev to give the dev server a separate state directory, then run wrangler d1 execute --persist-to ./.wrangler/exec against a different directory so the two don’t share state.

Symptom:

TypeError: Cannot read property 'prepare' of undefined
at fromD1

Cause: wrangler.jsonc doesn’t have a d1_databases binding matching the name you’re using in Env.

Fix:

{
"d1_databases": [
{
"binding": "DB", // ← this must match env.DB
"database_name": "my-app-prod",
"database_id": "<from wrangler d1 create output>"
}
]
}

Then redeploy:

Terminal window
pnpm wrangler deploy

The binding string is what appears on env in your Worker. If you write env.MY_DB in code, the binding must be "MY_DB".

Symptom:

TypeError: Cannot read property 'put' of undefined
at r2Storage.upload

Same cause and fix pattern as D1:

{
"r2_buckets": [
{
"binding": "BUCKET",
"bucket_name": "my-app-blobs"
}
]
}

Then create the bucket if you haven’t:

Terminal window
pnpm wrangler r2 bucket create my-app-blobs

Symptom: You’re firing push requests but wrangler tail shows no output.

Common causes:

  • Wrong environment. wrangler tail my-app tails the deployed Worker; local wrangler dev has its own console output. If you’re testing locally, check your terminal running wrangler dev.
  • Observability disabled. Add "observability": { "enabled": true } to wrangler.jsonc and redeploy.
  • Requests hitting cache. If you have Cloudflare cache rules in front of /sync/pull, cached responses skip the Worker entirely. Rare in practice — plasma’s pull responses shouldn’t be cache- friendly (each caller has different cookie params).

Symptom:

Error: Durable Object namespace not defined: SyncCoordinator

Cause: wrangler.jsonc declares the DO binding but the migration tag hasn’t been applied.

Fix:

{
"durable_objects": {
"bindings": [
{ "name": "COORDINATOR", "class_name": "SyncCoordinator" }
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["SyncCoordinator"]
}
]
}

Then:

Terminal window
pnpm wrangler deploy

The migrations[].tag is a string identifier — bump it (v2, v3) whenever you change the DO class structure.

Symptom: Deploy completes, curl https://my-app.<subdomain>.workers.dev/sync/push returns 404.

Causes:

  • Missing route. Custom domain deploys need a route defined in the Cloudflare dashboard. Without it, the Worker is only reachable at .workers.dev.
  • Wrong worker name. Check wrangler.jsonc’s name field matches the subdomain path.
  • main points at the wrong file. "main": "./worker.ts" must match the file that exports the default fetch handler.

Symptom:

TypeError: node:crypto: not implemented

Cause: plasma uses crypto.subtle (available) but some code paths reach for node:crypto transitively via a dependency.

Fix:

{
"compatibility_flags": ["nodejs_compat"]
}

Symptom: env.MY_VAR is undefined at runtime.

Fix:

For non-secret vars, declare in wrangler.jsonc:

{
"vars": {
"MY_VAR": "value"
}
}

For secrets:

Terminal window
pnpm wrangler secret put MY_VAR

Both appear on env at runtime. TypeScript needs a manual declaration:

interface Env {
DB: D1Database
BUCKET: R2Bucket
MY_VAR: string
MY_SECRET: string
}