runbook-pwa-outbox-stuck

type/runbook

Runbook — PWA outbox won't drain after reconnect

Symptoms

  • Pending indicator on transactions / banks / categories stays lit even though the device is back online.
  • New writes work; older queued writes never reach the server.
  • DevTools shows entries in IndexedDB under the app's database name.

Diagnose

  1. Network: is the app actually online? Service worker may be serving cached responses. DevTools → Application → Service Workers → check status.
  2. IndexedDB: DevTools → Application → IndexedDB → open the app DB → inspect the outbox store. Look for entries with old timestamps and an error/retry field.
  3. Sync hook: useOutboxSync should kick on online event. Check console for sync errors.
  4. Server response: a 4xx (e.g. validation failure on a stale schema) will keep retrying forever. Check src/lib/offline/sync.ts retry policy and watch network tab for the actual failing request.

Fix

Bad payload (most common)

A queued entity references something deleted server-side (e.g. category that no longer exists). Two options:

  • Edit the IndexedDB entry directly in DevTools to point at a valid category, then trigger sync.
  • Delete the stuck entry from the outbox store and re-enter the transaction manually.

Service worker stale

DevTools → Application → Service Workers → Update or Unregister, then hard reload (Cmd+Shift+R). On next load the new SW takes over.

Server-side schema drift

If a model field was renamed/removed since the device queued the write, the server merge will reject it. Either roll the server back, ship a tolerant merge handler in src/lib/offline/merge*.ts, or drop the entry.

Prevention

  • New entity types must register in entity-dispatch.ts and have merge logic in src/lib/offline/merge*Outbox.ts.
  • Server-side merges should be idempotent — outbox can replay the same write.

Related

  • [[Projects/personal-finance-notion/changelog/2026-04-19-offline-first-writes|Offline-first writes changelog]]