-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[Due for payment 2026-04-14] [$250] [Sentry] fix The database connection is closing issue #84192
Description
Sentry issue https://expensify.sentry.io/issues/7092239780
Thread https://expensify.slack.com/archives/C05LX9D6E07/p1770496102994319
Please re-state the problem that we are trying to solve in this issue.
When Onyx operations attempt to create an IndexedDB transaction, the browser may have already closed the database connection, causing:
InvalidStateError: Failed to execute 'transaction' on 'IDBDatabase': The database connection is closing.
This affects ~58,700 occurrences across 430 users on all browsers (Chrome: 31K, Safari: 18K, Firefox: 5K, Mobile Safari: 3K, Edge: 1K). The error is an unhandled promise rejection.
What is the root cause of that problem?
The existing db.onclose handler (line 22) sets dbp = undefined when the browser closes the connection, which protects future calls to getDB(). However, any operations that have already resolved the dbp promise still hold a stale db reference and proceed to call db.transaction() at line 59 — which throws InvalidStateError on the now-closing connection.
This can be triggered by:
• The browser closing idle IDB connections (Safari is especially aggressive, but Chrome/Firefox do this too under memory pressure or tab suspension)
• Another tab triggering a version upgrade (versionchange event — currently unhandled)
• The verifyStoreExists path closing the DB (line 39) while concurrent operations still hold the old reference
What changes do you think we should make in order to solve the problem?
All changes are in lib/storage/providers/IDBKeyValProvider/createStore.ts:
-
Retry mechanism with a single retry on InvalidStateError
— Extract the transaction chain (getDB → verifyStoreExists → db.transaction) into openTransactionAndExecute()
— Wrap the call in .catch(): if the error is InvalidStateError, reset dbp = undefined and retry once with a fresh connection
— If the retry also fails, the error is thrown normally and still surfaces in Sentry exactly as it does today -
Diagnostic logging on every retry (does NOT hide the issue)
— Log at alert level with: dbName, storeName, txMode, and closedBy
— closedBy tracks why the connection was closed: 'browser' (user-agent closed it), 'versionchange' (cross-tab upgrade), 'verifyStoreExists' (missing store path), or 'unknown'
— This gives us more visibility than today (where we only get a crash count with no context) -
onversionchange handler
— When another tab triggers a DB version upgrade, proactively close the connection and reset dbp
— Without this, cross-tab upgrades can block indefinitely and in-flight operations crash on the stale connection -
onclose handler now logs
— The existing onclose handler now logs when the browser unexpectedly closes the connection, giving us data on browser-initiated closures even when no retry is needed
Key safety guarantees:
• Data is never lost — the transaction fails before any data is written; the retry completes successfully with a valid connection
• Only one retry is attempted — no infinite loops
• If the retry fails, the error propagates normally and Sentry captures it as before
• Normal operations (no InvalidStateError) are completely unaffected — zero performance impact
• The closedBy logging gives us instrumentation to continue investigating the root cause of the spike
cc @fabioh8010
Issue Owner
Current Issue Owner: @mkhutornyiUpwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~022029317821194916452
- Upwork Job ID: 2029317821194916452
- Last Price Increase: 2026-03-04
Metadata
Metadata
Labels
Type
Fields
Give feedbackProjects
Status