Repository navigation
Description
Coming from #87862 (comment).
Issue
The tryOrDegradePerformance function in lib/storage/index.ts:31-55 wraps every storage operation with a try/catch intended to detect known critical errors and trigger a graceful fallback via degradePerformance. However, the try/catch only catches synchronous throws — and every storage operation in Onyx is asynchronous, so the fallback path is effectively dead code.
function tryOrDegradePerformance<T>(fn: () => Promise<T> | T, waitForInitialization = true): Promise<T> {
return new Promise<T>((resolve, reject) => {
const promise = waitForInitialization ? initPromise : Promise.resolve();
promise.then(() => {
try {
resolve(fn()); // fn() returns a Promise — rejection escapes
} catch (error) {
// This catch only fires on synchronous throws.
// IDB operations reject asynchronously — this block never executes.
if (error instanceof Error) {
if (error.message.includes('Internal error opening backing store for indexedDB.open')) {
degradePerformance(error);
}
if (error.message.includes('IDBKeyVal store could not be created')) {
degradePerformance(error);
}
}
reject(error);
}
});
});
}Because resolve(fn()) schedules the resolution of the outer Promise with the inner Promise's eventual value rather than awaiting it, any rejection from fn() (e.g. Storage.setItem, Storage.multiSet) is propagated through the Promise chain — it never re-enters the catch block. The intended degradation logic is unreachable.
Notably, the existing string check for 'Internal error opening backing store for indexedDB.open' was meant to handle the exact error analyzed in #87862, but it has never fired in production for this reason.
Solution
Convert the synchronous try/catch into a Promise .catch(...) so async rejections are actually caught:
function tryOrDegradePerformance<T>(fn: () => Promise<T> | T, waitForInitialization = true): Promise<T> {
const promise = waitForInitialization ? initPromise : Promise.resolve();
return promise.then(() =>
Promise.resolve(fn()).catch((error) => {
if (error instanceof Error && error.message.includes('IDBKeyVal store could not be created')) {
degradePerformance(error);
}
return Promise.reject(error);
}),
);
}While making this change, remove the 'Internal error opening backing store for indexedDB.open' string check from this function. Per the parent comment, this error class is permanent corruption and won't recover from a provider swap — it gets routed to a dedicated heal path (separate action item #90636) instead of degradePerformance. Leaving the check in would conflict with that heal mechanism.
The other branch ('IDBKeyVal store could not be created') can stay as-is for now — it's a different class (init failure), and routing it to degradePerformance is still the intended behavior.
Test plan
- Add a unit test that confirms a Promise rejection from
fn()(e.g. a mockedStorage.setItemthat rejects withnew Error('IDBKeyVal store could not be created')) now triggersdegradePerformance. - Confirm existing storage tests still pass.
- Verify in production logs that the
Logger.logHmmm("Error while using ${provider.name}. Falling back to only using cache and dropping storage.")message starts appearing where appropriate (currently appears 0 times).
Issue Owner
Current Issue Owner: @elirangoshenMetadata
Metadata
Assignees
Type
Projects
- StatusShow more project fieldsDone
Activity
UnknownError: Internal error opening backing store for indexedDB.open.storage error #87862elirangoshen commentedon May 14, 2026
Hi, Im Eliran from callstack, I can work on this issue .
elirangoshen commentedon May 14, 2026
Update: I opened pr , and working to address potential comments
elirangoshen commentedon May 18, 2026
Update: I addressed comments in my pr and waiting for feedback
Julesssss commentedon May 19, 2026
RN PR merged, awaiting bump, we have quite a long queue currently.
melvin-bot commentedon May 21, 2026
@elirangoshen Whoops! This issue is 2 days overdue. Let's get this updated quick!
elirangoshen commentedon May 22, 2026
It merged already
1 remaining item
melvin-bot commentedon May 25, 2026
@elirangoshen Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
elirangoshen commentedon May 26, 2026
we wait for onyx bump, as the pr got merged
melvin-bot commentedon May 29, 2026
@elirangoshen Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
elirangoshen commentedon Jun 1, 2026
Waiting for onyx bump
[-][Onyx] Fix dead-code async-catch in tryOrDegradePerformance[/-][+][Due for payment 2026-06-11] [Onyx] Fix dead-code async-catch in tryOrDegradePerformance[/+]melvin-bot commentedon Jun 4, 2026
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.3.98-3 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
If no regressions arise, payment will be issued on 2026-06-11. 🎊
For reference, here are some details about the assignees on this issue:
cristipaval commentedon Jun 4, 2026
No payment needed here. Closing