From dd95a4e06d5977a0b014d5e4f22bdf8c3c534bb0 Mon Sep 17 00:00:00 2001 From: Elie Habib Date: Thu, 23 Apr 2026 11:47:20 +0400 Subject: [PATCH] fix(idb-cleanup): swallow TransactionInactiveError in idempotent IDB cursor loops (#3335) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sentry WORLDMONITOR-NX: iOS Safari kills in-flight IDB transactions when the tab backgrounds. Our idle detector fires `[App] User idle - pausing animations to save resources` right before the browser suspends — any `cursor.delete()` / `cursor.continue()` mid-iteration then throws TransactionInactiveError synchronously inside onsuccess. Both affected sites are idempotent cleanup (`cleanOldSnapshots`, `deleteFromIndexedDbByPrefix`); swallowing the throw lets the next run resume from where we left off. `main.ts` beforeSend keeps TransactionInactiveError surfaced for first-party stacks (storage.ts, persistent-cache.ts, vector-db.ts), so this is the correct layer to handle the background-kill case. --- src/services/persistent-cache.ts | 10 +++++++--- src/services/storage.ts | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/services/persistent-cache.ts b/src/services/persistent-cache.ts index 0208f836d..6b1d9cc5a 100644 --- a/src/services/persistent-cache.ts +++ b/src/services/persistent-cache.ts @@ -82,9 +82,13 @@ async function deleteFromIndexedDbByPrefix(prefix: string): Promise { request.onsuccess = () => { const cursor = request.result; if (!cursor) return; - - store.delete(cursor.primaryKey); - cursor.continue(); + // iOS Safari kills in-flight IDB transactions when the tab backgrounds; + // prefix-invalidation is idempotent so swallow TransactionInactiveError + // and let the next invalidation call resume. + try { + store.delete(cursor.primaryKey); + cursor.continue(); + } catch { /* tx died mid-iteration */ } }; request.onerror = () => reject(request.error); }); diff --git a/src/services/storage.ts b/src/services/storage.ts index ae7992ccc..934b1590f 100644 --- a/src/services/storage.ts +++ b/src/services/storage.ts @@ -214,7 +214,10 @@ export async function cleanOldSnapshots(): Promise { const request = store.index('by_time').openCursor(IDBKeyRange.upperBound(cutoff)); request.onsuccess = () => { const cursor = request.result; - if (cursor) { cursor.delete(); cursor.continue(); } + if (!cursor) return; + // iOS Safari kills in-flight IDB transactions when the tab backgrounds; + // cleanup is idempotent so swallow TransactionInactiveError and resume next run. + try { cursor.delete(); cursor.continue(); } catch { /* tx died mid-cleanup */ } }; void tx; },