You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-07-23] [$500] [Sentry: APP-B2Q] HybridApp iOS C++ JSError across JSI bridge #93845
⚠️Preliminary analysis (low confidence on root cause) — this crash signature is a generic bucket, and the specific JS error can't be pinned without reading a Sentry event. Treat the next steps as the actionable part.
Issue Analysis
What this signature actually is:C++ Exception: N8facebook3jsi7JSErrorE (facebook::jsi::JSError) is not a distinct bug — it's the catch-all that appears whenever any unhandled JavaScript exception propagates across the JSI native boundary on HybridApp iOS. The C++ frames are the native bridge unwinding; they are never actionable on their own. This is why the issue shows affected route: unknown and bundles 100 users / 200 events — many unrelated JS errors are grouped under the single C++ frame.
Why the current data isn't enough: The "top frames, first-party only" block only contains the native side. The real signal — the JS exception class (e.g. RangeError/TypeError), its message, and the symbolicated JS stack — lives inside the Sentry events for APP-B2Q, not in this issue body. The issue's own "Suspected cause" already notes this. I attempted to pull the events via the Sentry integration but it isn't authenticated in this automated run, so I couldn't retrieve the JS stack.
Precedent (what these usually turn out to be): The last time an identical facebook::jsi::JSError crash was filed on HybridApp iOS (Expensify/App#51853), the underlying JS error was RangeError: Maximum call stack size exceeded from infinite recursion in getRootParentReport. That specific path is now guarded with cycle detection at src/libs/ReportUtils.ts:1346-1352, so this is a different unhandled exception (or a new recursion source) — not a regression of that fix.
Fix location is unknown until the JS stack is read. Because this is HybridApp iOS, the throwing code could be in App JS or in the Mobile-Expensify native/bridge layer. Per repo policy I won't propose a code fix for a root cause I can't locate.
Next Steps for Contributor+ team:
Open a recent event under Sentry APP-B2Q and read the full exception. Sentry stores the real message (Unhandled JS Exception: <ErrorClass>: <message> ... stack: <jsFn>@1:... ) and the symbolicated JS frames on the event even though the grouping title only shows the C++ frame.
That message gives the exact error class + the throwing JS function. If multiple distinct JS errors appear across events, this bucket should be split rather than fixed as one.
Once the throwing function is known, decide whether the fix belongs in Expensify/App (JS) or Mobile-Expensify (native) and re-run analysis.
To refine: Reply @MelvinBot the JS exception from Sentry is: <paste message + stack> and I'll pinpoint the root cause and propose a fix.
APP-B2Q isn't a single bug — it's the catch-all bucket for unhandled JS exceptions that escape into native on iOS HybridApp. C++ Exception: N8facebook3jsi7JSErrorE with mechanism cpp_exception is precisely how Hermes surfaces a JS throw that nothing on the JS side caught: the exception is rethrown across the JSI HostFunction boundary into the OldDot native host and aborts the process. We've already fixed two instances of this exact signature one-off — #57786 (Unhandled JS Exception: TypeError … 'signupQualifier' of undefined, with a stack through getOnboardingInitialPath → startOnboardingFlow → Onyx's processNext/_processUpdate) and #51853. APP-B2Q is the remainder that never got a JS stack attached, which is why it reads as "route: unknown (JSI bridge)".
Two things make this a recurring, opaque group:
1. The only error boundary is render-scoped
BaseErrorBoundary wraps react-error-boundary, which by design only catches errors thrown during React render/lifecycle. The throws behind these crashes fire in non-render contexts — Onyx connection/update callbacks, navigation init, event handlers, async tasks. That's exactly the getOnboardingInitialPath path from #57786: OnboardingFlow.ts, where currentOnboardingValues?.signupQualifier is now read through ?. after that crash. The boundary never sees these.
2. There is no native catch-all
On web, ErrorBoundary/index.tsx registers window.addEventListener('unhandledrejection', …), so a stray rejection gets logged instead of being fatal. The native boundary ErrorBoundary/index.native.tsx registers nothing equivalent, and there is no ErrorUtils.setGlobalHandler anywhere in src/. So an uncaught throw on native has no JS-side net — it goes straight to Hermes' default fatal path and across the bridge as jsi::JSError.
Why the group has no JS frames
In HybridApp the JS-side Sentry.init runs with autoInitializeNativeSdk: false — see setupSentry.ts — so the native Sentry SDK (AppDelegate) owns crash capture. When the throw aborts as a C++ exception, it's the native SDK that records it, carrying only the N8facebook3jsi7JSErrorE frame and none of the JS stack the @sentry/react-native JS error handler would have attached. That's why every sibling row in the #91302 tracker names a specific culprit and this one can't.
What changes do you think we should make in order to solve the problem?
I'd split this into "make it actionable" and "stop it being fatal".
1. Attach JS stacks so the group can be split (this is what the issue asks for)
Add a JS-side capture for uncaught/fatal errors on native so the JS stack is recorded before the native C++ frame is all that survives — register a global handler in the native ErrorBoundary layer (index.native.tsx) that mirrors web's unhandledrejection listener and calls Sentry.captureException(error) with the original JS error. In parallel, confirm Hermes sourcemaps for the 9.4.5–9.4.9 iOS HybridApp release bundles are uploaded to Sentry so those JS frames symbolicate. Layer: telemetry/config + native ErrorBoundary entry point. Once frames land, APP-B2Q fans out into per-culprit trackers like its siblings.
2. Add the missing native safety net (generalize the one-off guards)
Install an ErrorUtils.setGlobalHandler / unhandled-rejection analog on native that logs the JS error (with stack) as a handled Sentry event and, for non-fatal throws, keeps the app alive — the native counterpart of the web unhandledrejection net that already exists. This stops us from patching each new x of undefined site (#57786, #51853) one production crash at a time.
I'll be honest about scope: the specific throwing line(s) behind APP-B2Q can only be pinned from the Sentry events themselves — that's the literal meaning of the issue's "needs symbolicated JS stack". Step 1 is what unblocks that; #57786 shows the per-site fix is then trivial (a ?. or a default).
What alternative solutions did you explore? (Optional)
Wrap the Onyx update queue (processNext / _processUpdate) in try/catch so callback throws can't go fatal. Rejected as primary: it only covers the Onyx entry point (one of several paths feeding this group), and swallowing errors there can leave Onyx state half-applied. A top-level global handler is the right altitude.
// Native method can send non-null value only once per NewDot lifecycle. It prevents issues with multiple initializations during reloads on debug builds.
Log.info('[HybridApp] `getHybridAppSettings` called more than once during single NewDot lifecycle. Skipping initialization.');
Causal chain: OldDot passes a settings string that is non-null but malformed (truncated/corrupted across the native handoff) → JSON.parse throws synchronously inside .then → the promise rejects → there is no .catch anywhere in the chain → the unhandled rejection surfaces on Hermes/JSI during startup as a native facebook::jsi::JSError (cpp_exception). The same .then block then assumes a well-formed object and dereferences hybridAppSettings.hybridApp.loggedOutFromOldDot, so even a structurally-valid-but-incomplete payload throws the same way.
What changes do you think we should make in order to solve the problem?
Make the parse fail safe at the single convergence point. Wrap the JSON.parse in parseHybridAppSettings in a try/catch, log the failure, and return null so the existing if (!hybridAppSettings) skip path in the handler takes over instead of crashing:
In words: if parsing throws, Log the error and return null. Because every caller already routes through getHybridAppSettings, this one change protects all entry points.
As a defensive backstop, also attach a .catch to the chain in HybridAppHandler so any rejection (or a thrown property access in the .then) is logged rather than left unhandled:
// Native method can send non-null value only once per NewDot lifecycle. It prevents issues with multiple initializations during reloads on debug builds.
Log.info('[HybridApp] `getHybridAppSettings` called more than once during single NewDot lifecycle. Skipping initialization.');
What alternative solutions did you explore? (Optional)
Validate the shape of the parsed object (presence of hybridApp) before use and treat a missing/invalid shape the same as null. This addresses the secondary dereference crash but is more code than the try/catch and the .catch already cover.
⚠️@lorretheboy Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).
Please re-state the problem that we are trying to solve in this issue.
On iOS HybridApp, a JS exception escapes into native as C++ Exception: N8facebook3jsi7JSErrorE (mechanism: cpp_exception) with no symbolicated JS stack, so APP-B2Q is an opaque catch-all that can't be triaged or split into per-culprit issues.
What is the root cause of that problem?
The app already has a net that attaches a JS stack to ordinary throws: the JS-thread handler wired by Sentry.wrap(App) (
) plus @sentry/react-native's default RN error-handler integration. That is exactly why the other HybridApp crashes of this family (#57786, #51853) landed with readable JS frames and were fixable one-off.
APP-B2Q is the remainder that never reaches that net: the throws that fire inside the OldDot→NewDot startup bridge continuations, which run as the direct follow-up of a native HybridApp call rather than as ordinary app code, and which have no error isolation of their own:
the getHybridAppSettings() consumer and finalizeTransitionFromOldDot, which unconditionally dereference hybridAppSettings.hybridApp.* —
// Native method can send non-null value only once per NewDot lifecycle. It prevents issues with multiple initializations during reloads on debug builds.
Log.info('[HybridApp] `getHybridAppSettings` called more than once during single NewDot lifecycle. Skipping initialization.');
setupNewDotAfterTransitionFromOldDot, which destructures and reaches into hybridApp.delegateAccessData?.* / runs Onyx.multiSet against the handed-off payload —
). So when one of these continuations throws, the error is re-raised across the JSI boundary and recorded by the native iOS Sentry SDK (the JS SDK runs with autoInitializeNativeSdk: false on HybridApp —
// In HybridApp, native SDK is initialized early in Application.onCreate (Android) and
) as a bare facebook::jsi::JSError, with no JS stack and "route: unknown".
What changes do you think we should make in order to solve the problem?
Isolate each HybridApp bridge continuation so a throw is captured with its JS stack (which is what makes APP-B2Q splittable — the issue's literal ask) and the app degrades gracefully instead of aborting. Add one shared guard and apply it at the bridge entry points:
// Runs a HybridApp bridge continuation in isolation: a throw is reported to Sentry WITH its JS// stack (so it no longer surfaces as a stack-less native `facebook::jsi::JSError`) and is swallowed// so the OldDot->NewDot transition degrades instead of crashing.functionrunHybridAppBridgeStep<T>(label: string,step: ()=>T): T|undefined{try{returnstep();}catch(error){Sentry.captureException(error,{tags: {hybridAppBridgeStep: label}});Log.alert(`[HybridApp] Uncaught error in bridge step "${label}"`,{error: String(error)});returnundefined;}}
Wrap the getHybridAppSettings().then(...) body and finalizeTransitionFromOldDot in HybridAppHandler with it, and do the same for the synchronous bodies of the HybridApp Onyx subscribers in src/libs/actions/HybridApp/index.ts. Because Sentry.captureException runs on the JS thread with the original Error, the event now carries the symbolicated JS frames the native SDK was dropping — once frames land, APP-B2Q fans out into per-culprit trackers like its siblings, and each is then a trivial ?./default fix.
Note: the exact throwing field/line can only be confirmed from a symbolicated event — that's the meaning of the issue's "needs symbolicated JS stack" — and the capture above is precisely what restores it. To also symbolicate any other JS frames, confirm the Hermes sourcemaps for the 9.4.5–9.4.9 iOS HybridApp bundles are uploaded to Sentry.
What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?
Cover that a malformed/incomplete handoff payload does not crash the transition: when hybridAppSettings.hybridApp (or a delegateAccessData field) is missing, finalizeTransitionFromOldDot / setupNewDotAfterTransitionFromOldDot should call Sentry.captureException once and resolve (transition skipped) rather than throw. Assert the guard swallows the error and the app continues.
What alternative solutions did you explore? (Optional)
Installing a JS global net (ErrorUtils.setGlobalHandler and/or a native unhandledrejection analog). It's worth adding for the broader surface, but it does not reliably execute in a native-invoked bridge continuation, so it wouldn't guarantee a JS stack for this specific class — capturing at the bridge step does.
Guarding only the JSON.parse in parseHybridAppSettings and returning null. That hardens the parse step, but the same payload is dereferenced again downstream in setupNewDotAfterTransitionFromOldDot (hybridApp.delegateAccessData?.*, Onyx.multiSet), so wrapping the continuation covers both the parse and the downstream access at one altitude.
changed the title [-][Due for payment 2026-07-23] [$250] [Sentry: APP-B2Q] HybridApp iOS C++ JSError across JSI bridge[/-][+][Due for payment 2026-07-23] [$500] [Sentry: APP-B2Q] HybridApp iOS C++ JSError across JSI bridge[/+]on Jul 21, 2026
Activity
melvin-bot commentedon Jun 17, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @ahmedGaber93 (
External)melvin-bot commentedon Jun 17, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022067266336125879225
MelvinBot commentedon Jun 17, 2026
Issue Analysis
What this signature actually is:
C++ Exception: N8facebook3jsi7JSErrorE(facebook::jsi::JSError) is not a distinct bug — it's the catch-all that appears whenever any unhandled JavaScript exception propagates across the JSI native boundary on HybridApp iOS. The C++ frames are the native bridge unwinding; they are never actionable on their own. This is why the issue shows affected route: unknown and bundles 100 users / 200 events — many unrelated JS errors are grouped under the single C++ frame.Why the current data isn't enough: The "top frames, first-party only" block only contains the native side. The real signal — the JS exception class (e.g.
RangeError/TypeError), its message, and the symbolicated JS stack — lives inside the Sentry events for APP-B2Q, not in this issue body. The issue's own "Suspected cause" already notes this. I attempted to pull the events via the Sentry integration but it isn't authenticated in this automated run, so I couldn't retrieve the JS stack.Precedent (what these usually turn out to be): The last time an identical
facebook::jsi::JSErrorcrash was filed on HybridApp iOS (Expensify/App#51853), the underlying JS error wasRangeError: Maximum call stack size exceededfrom infinite recursion ingetRootParentReport. That specific path is now guarded with cycle detection atsrc/libs/ReportUtils.ts:1346-1352, so this is a different unhandled exception (or a new recursion source) — not a regression of that fix.Fix location is unknown until the JS stack is read. Because this is HybridApp iOS, the throwing code could be in App JS or in the Mobile-Expensify native/bridge layer. Per repo policy I won't propose a code fix for a root cause I can't locate.
Next Steps for Contributor+ team:
Unhandled JS Exception: <ErrorClass>: <message> ... stack: <jsFn>@1:...) and the symbolicated JS frames on the event even though the grouping title only shows the C++ frame.Expensify/App(JS) orMobile-Expensify(native) and re-run analysis.To refine: Reply
@MelvinBot the JS exception from Sentry is: <paste message + stack>and I'll pinpoint the root cause and propose a fix.Abdulloh0109 commentedon Jun 17, 2026
What is the root cause of that problem?
APP-B2Q isn't a single bug — it's the catch-all bucket for unhandled JS exceptions that escape into native on iOS HybridApp.
C++ Exception: N8facebook3jsi7JSErrorEwith mechanismcpp_exceptionis precisely how Hermes surfaces a JS throw that nothing on the JS side caught: the exception is rethrown across the JSI HostFunction boundary into the OldDot native host and aborts the process. We've already fixed two instances of this exact signature one-off — #57786 (Unhandled JS Exception: TypeError … 'signupQualifier' of undefined, with a stack throughgetOnboardingInitialPath→startOnboardingFlow→ Onyx'sprocessNext/_processUpdate) and #51853. APP-B2Q is the remainder that never got a JS stack attached, which is why it reads as "route: unknown (JSI bridge)".Two things make this a recurring, opaque group:
1. The only error boundary is render-scoped
BaseErrorBoundarywrapsreact-error-boundary, which by design only catches errors thrown during React render/lifecycle. The throws behind these crashes fire in non-render contexts — Onyx connection/update callbacks, navigation init, event handlers, async tasks. That's exactly thegetOnboardingInitialPathpath from #57786: OnboardingFlow.ts, wherecurrentOnboardingValues?.signupQualifieris now read through?.after that crash. The boundary never sees these.2. There is no native catch-all
On web,
ErrorBoundary/index.tsxregisterswindow.addEventListener('unhandledrejection', …), so a stray rejection gets logged instead of being fatal. The native boundaryErrorBoundary/index.native.tsxregisters nothing equivalent, and there is noErrorUtils.setGlobalHandleranywhere insrc/. So an uncaught throw on native has no JS-side net — it goes straight to Hermes' default fatal path and across the bridge asjsi::JSError.Why the group has no JS frames
In HybridApp the JS-side
Sentry.initruns withautoInitializeNativeSdk: false— seesetupSentry.ts— so the native Sentry SDK (AppDelegate) owns crash capture. When the throw aborts as a C++ exception, it's the native SDK that records it, carrying only theN8facebook3jsi7JSErrorEframe and none of the JS stack the@sentry/react-nativeJS error handler would have attached. That's why every sibling row in the #91302 tracker names a specific culprit and this one can't.What changes do you think we should make in order to solve the problem?
I'd split this into "make it actionable" and "stop it being fatal".
1. Attach JS stacks so the group can be split (this is what the issue asks for)
Add a JS-side capture for uncaught/fatal errors on native so the JS stack is recorded before the native C++ frame is all that survives — register a global handler in the native ErrorBoundary layer (
index.native.tsx) that mirrors web'sunhandledrejectionlistener and callsSentry.captureException(error)with the original JS error. In parallel, confirm Hermes sourcemaps for the 9.4.5–9.4.9 iOS HybridApp release bundles are uploaded to Sentry so those JS frames symbolicate. Layer: telemetry/config + native ErrorBoundary entry point. Once frames land, APP-B2Q fans out into per-culprit trackers like its siblings.2. Add the missing native safety net (generalize the one-off guards)
Install an
ErrorUtils.setGlobalHandler/ unhandled-rejection analog on native that logs the JS error (with stack) as a handled Sentry event and, for non-fatal throws, keeps the app alive — the native counterpart of the webunhandledrejectionnet that already exists. This stops us from patching each newx of undefinedsite (#57786, #51853) one production crash at a time.I'll be honest about scope: the specific throwing line(s) behind APP-B2Q can only be pinned from the Sentry events themselves — that's the literal meaning of the issue's "needs symbolicated JS stack". Step 1 is what unblocks that; #57786 shows the per-site fix is then trivial (a
?.or a default).What alternative solutions did you explore? (Optional)
processNext/_processUpdate) in try/catch so callback throws can't go fatal. Rejected as primary: it only covers the Onyx entry point (one of several paths feeding this group), and swallowing errors there can leave Onyx state half-applied. A top-level global handler is the right altitude.lorretheboy commentedon Jun 17, 2026
Proposal
What is the root cause of that problem?
At HybridApp startup,
HybridAppHandlerrequests the settings string that OldDot hands to NewDot and parses it. The parse is an unguardedJSON.parse:App/src/libs/actions/HybridApp/index.ts
Lines 78 to 84 in 6ea10e7
This runs inside the promise chain returned by
getHybridAppSettings:App/src/libs/actions/HybridApp/index.ts
Lines 86 to 90 in 6ea10e7
and the consumer attaches a
.thenbut no.catch:App/src/HybridAppHandler.tsx
Lines 40 to 73 in 6ea10e7
Causal chain: OldDot passes a settings string that is non-null but malformed (truncated/corrupted across the native handoff) →
JSON.parsethrows synchronously inside.then→ the promise rejects → there is no.catchanywhere in the chain → the unhandled rejection surfaces on Hermes/JSI during startup as a nativefacebook::jsi::JSError(cpp_exception). The same.thenblock then assumes a well-formed object and dereferenceshybridAppSettings.hybridApp.loggedOutFromOldDot, so even a structurally-valid-but-incomplete payload throws the same way.What changes do you think we should make in order to solve the problem?
Make the parse fail safe at the single convergence point. Wrap the
JSON.parseinparseHybridAppSettingsin atry/catch, log the failure, and returnnullso the existingif (!hybridAppSettings)skip path in the handler takes over instead of crashing:App/src/libs/actions/HybridApp/index.ts
Lines 78 to 84 in 6ea10e7
In words: if parsing throws,
Logthe error and returnnull. Because every caller already routes throughgetHybridAppSettings, this one change protects all entry points.As a defensive backstop, also attach a
.catchto the chain inHybridAppHandlerso any rejection (or a thrown property access in the.then) is logged rather than left unhandled:App/src/HybridAppHandler.tsx
Lines 40 to 73 in 6ea10e7
What alternative solutions did you explore? (Optional)
Validate the shape of the parsed object (presence of
hybridApp) before use and treat a missing/invalid shape the same asnull. This addresses the secondary dereference crash but is more code than thetry/catchand the.catchalready cover.github-actions commentedon Jun 17, 2026
David-2002065 commentedon Jun 17, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
On iOS HybridApp, a JS exception escapes into native as
C++ Exception: N8facebook3jsi7JSErrorE(mechanism: cpp_exception) with no symbolicated JS stack, soAPP-B2Qis an opaque catch-all that can't be triaged or split into per-culprit issues.What is the root cause of that problem?
The app already has a net that attaches a JS stack to ordinary throws: the JS-thread handler wired by
Sentry.wrap(App)(App/src/App.tsx
Lines 141 to 143 in 82b779a
@sentry/react-native's default RN error-handler integration. That is exactly why the other HybridApp crashes of this family (#57786, #51853) landed with readable JS frames and were fixable one-off.APP-B2Qis the remainder that never reaches that net: the throws that fire inside the OldDot→NewDot startup bridge continuations, which run as the direct follow-up of a native HybridApp call rather than as ordinary app code, and which have no error isolation of their own:getHybridAppSettings()consumer andfinalizeTransitionFromOldDot, which unconditionally dereferencehybridAppSettings.hybridApp.*—App/src/HybridAppHandler.tsx
Lines 20 to 73 in 82b779a
setupNewDotAfterTransitionFromOldDot, which destructures and reaches intohybridApp.delegateAccessData?.*/ runsOnyx.multiSetagainst the handed-off payload —App/src/libs/actions/Session/index.ts
Lines 665 to 734 in 82b779a
App/src/libs/actions/HybridApp/index.ts
Lines 36 to 73 in 82b779a
None of these blocks is wrapped in
try/catch, and the app registers no JS-thread net on native equivalent to the webunhandledrejectionlistener (web:App/src/components/ErrorBoundary/index.tsx
Lines 20 to 25 in 82b779a
App/src/components/ErrorBoundary/index.native.tsx
Lines 6 to 15 in 82b779a
autoInitializeNativeSdk: falseon HybridApp —App/src/setup/telemetry/setupSentry.ts
Lines 29 to 31 in 82b779a
facebook::jsi::JSError, with no JS stack and "route: unknown".What changes do you think we should make in order to solve the problem?
Isolate each HybridApp bridge continuation so a throw is captured with its JS stack (which is what makes
APP-B2Qsplittable — the issue's literal ask) and the app degrades gracefully instead of aborting. Add one shared guard and apply it at the bridge entry points:Wrap the
getHybridAppSettings().then(...)body andfinalizeTransitionFromOldDotinHybridAppHandlerwith it, and do the same for the synchronous bodies of the HybridApp Onyx subscribers insrc/libs/actions/HybridApp/index.ts. BecauseSentry.captureExceptionruns on the JS thread with the originalError, the event now carries the symbolicated JS frames the native SDK was dropping — once frames land,APP-B2Qfans out into per-culprit trackers like its siblings, and each is then a trivial?./default fix.Note: the exact throwing field/line can only be confirmed from a symbolicated event — that's the meaning of the issue's "needs symbolicated JS stack" — and the capture above is precisely what restores it. To also symbolicate any other JS frames, confirm the Hermes sourcemaps for the 9.4.5–9.4.9 iOS HybridApp bundles are uploaded to Sentry.
What specific scenarios should we cover in automated tests to prevent reintroducing this issue in the future?
Cover that a malformed/incomplete handoff payload does not crash the transition: when
hybridAppSettings.hybridApp(or adelegateAccessDatafield) is missing,finalizeTransitionFromOldDot/setupNewDotAfterTransitionFromOldDotshould callSentry.captureExceptiononce and resolve (transition skipped) rather than throw. Assert the guard swallows the error and the app continues.What alternative solutions did you explore? (Optional)
ErrorUtils.setGlobalHandlerand/or a nativeunhandledrejectionanalog). It's worth adding for the broader surface, but it does not reliably execute in a native-invoked bridge continuation, so it wouldn't guarantee a JS stack for this specific class — capturing at the bridge step does.JSON.parseinparseHybridAppSettingsand returningnull. That hardens the parse step, but the same payload is dereferenced again downstream insetupNewDotAfterTransitionFromOldDot(hybridApp.delegateAccessData?.*,Onyx.multiSet), so wrapping the continuation covers both the parse and the downstream access at one altitude.78 remaining items
ahmedGaber93 commentedon Jul 20, 2026
@mountiny I'd like to revisit the compensation based on the amount of work involved, since this PR fixes three different crashes.
Proposed amount to increase the price to ($500)
Multiple PRs
Thanks!
[-][Due for payment 2026-07-23] [$250] [Sentry: APP-B2Q] HybridApp iOS C++ JSError across JSI bridge[/-][+][Due for payment 2026-07-23] [$500] [Sentry: APP-B2Q] HybridApp iOS C++ JSError across JSI bridge[/+]melvin-bot commentedon Jul 21, 2026
Upwork job price has been updated to $500
mountiny commentedon Jul 21, 2026
Ok sounds good!
melvin-bot commentedon Jul 23, 2026
@mallenexpensify Whoops! This issue is 2 days overdue. Let's get this updated quick!
ahmedGaber93 commentedon Jul 24, 2026
@mallenexpensify The job is complete and ready for payment. Thanks!
mallenexpensify commentedon Jul 27, 2026
Payment Summary
Contributor+: @ahmedGaber93 due $500 via NewDot
flaviadefaria commentedon Sep 2, 2026
Approved: @ahmedGaber93 due $500 via NewDot