| Every share op 429s (or POST 503s) in production | Redis down / REDIS_URL unset — rate limiter fails closed in production; POST refuses shares whose token hash can't persist | Restore Redis; never weaken the fail-closed branches (api/lib/rateLimit.ts:118, api/share.ts:122) |
| Local dev: rate limiting seems absent | No Redis + VERCEL_ENV !== 'production' → allow-all by design | Expected; production denies instead |
| All OLD shares 401 on PUT/DELETE, new ones fine | TOKEN_SALT rotated or differs between envs | Restore original salt; no recovery for hashes made with a lost salt |
| Share endpoints 500 on any token op | TOKEN_SALT unset — hashToken throws | Set TOKEN_SALT in Vercel env |
| CONTENT_BLOCKED on innocent layout | A note/label contains a URL, 11+ repeated chars, or 5+ combining marks — HARMFUL_PATTERNS blocks all on raw text | By design; change patterns deliberately with tests, not as a "bug fix" |
| POST 409 "share with this ID already exists" | Share ID = layout UUID; blob exists | Client PUTs with stored deleteToken (useCloudShare handles via entry.cloudShare); never flip allowOverwrite |
| Liveblocks auth 400 "Missing user ID" on every join | authEndpoint in src/liveblocks.config.ts changed to a URL string — SDK then POSTs only {room} | Keep it a function POSTing {room, userId} (comment at line 98) |
| Everyone read-only despite 'edit' share | liveblocks-auth reads permission from the blob; share was created 'view', or scopes changed | Check metadata.permission in the blob and session.allow at api/liveblocks-auth.ts:172 |
| Viewer sees stale layout after owner edit | Auto-sync hadn't fired (5s debounce; staging-only edits never change createLayoutFingerprint); only for legacy 12-char IDs can the SW shared-layouts cache (7d) also serve stale | Wait out the debounce / make an on-grid edit; staging exclusion is by design (src/features/cloud-share/utils/cloudShare.ts) — though when a sync fires, the FULL layout including staging bins uploads. Hard reload only helps legacy IDs |
| PUT of designer-share params fails validation | api/share/[id].ts PUT only knows the layout schema (validateShareLayout); designer shares get permission-only PUTs | By design: delete + re-create, or add a designer branch to handlePut mirroring api/share.ts |
| Tests pass, deployed function crashes ERR_MODULE_NOT_FOUND | Relative import in api/ missing .js extension | Match existing style ('./lib/shared.js') |
| Auth userId ≠ presence userId | localStorage key gridfinity-user-id is read by BOTH src/liveblocks.config.ts and CollabProvider.tsx via separate implementations | Keep the two in sync |