Skip to content

[Due for payment 2026-06-12] [$250] [Sentry: APP-8Z0 / APP-8PM] HybridApp iOS EXC_BAD_ACCESS in FullStory native onSessionStarted  #91291

Closed

Description

@mountiny

Sentry

Two Sentry groupings for the same crash:

Impact (snapshot at filing, combined across both Sentry IDs)

  • Users (total since first seen): ~4,625 (APP-8Z0: 612 / APP-8PM: 4,013)
  • Events: ~4,712 (APP-8Z0: 614 / APP-8PM: 4,098)
  • Users (last 14d): ~74 (APP-8Z0: 44 / APP-8PM: 30)
  • First seen: 2026-03-27
  • Last seen: now (ongoing)
  • Platform: iOS, hybrid_app
  • App version(s): seen on 9.3.63 through 9.3.77
  • OS: iOS 26.x dominant; high-end devices (iPhone17, iPhone18)
  • Mechanism: mach, fatal

Stack trace (native, top frames)

main thread (UIApplicationMain)
  -> _CFRunLoop / _dispatch_main_queue_drain
  -> PopulateInclusiveFramesForLayer
  -> readSelectorMatch
  -> -[FullStory fullstoryDidStartSession:]
  -> -[NativeFullStorySpecBase emitOnSessionStarted:]
  -> std::__1::function<T>::operator()
  -> std::__1::__hash_table<T>::__emplace_unique_key_args<T>
KERN_INVALID_ADDRESS  (EXC_BAD_ACCESS)

Suspected cause

Memory access violation inside the FullStory iOS SDK when emitting the session-started event. The crash is fully below our app surface (no first-party frames in the stack), so the underlying fix likely belongs in a FullStory iOS SDK upgrade.

Investigation suggestions

  • Check what FullStory iOS SDK version we're currently shipping vs the latest release; look for fixes in the changelog around onSessionStarted / fullstoryDidStartSession.
  • Group by os.version and device.class in Sentry to see if it correlates with iOS 26.x specifically.
  • As a short-term mitigation, evaluate gating FullStory init on the affected OS / device subset once identified.

Reproduction

Unknown deterministically — fires on any session that reaches FullStory's onSessionStarted callback path. Sentry events are reproducible against the linked sessions in Fullstory dashboard (not pasted here to keep this issue free of identifying data — follow the link in each Sentry event).

Related

  • Sibling Sentry IDs covered by this issue: APP-8Z0, APP-8PM
  • Long tail of related single/few-user FullStory native variants: APP-8MH, APP-A78, APP-A35, APP-8WH, APP-9JE (all likely subsumed by the same SDK fix)
  • Prior GH issues: none found
Upwork Automation - Do Not Edit
Issue OwnerCurrent Issue Owner: @mallenexpensify

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on May 21, 2026
  2. self-assigned this
    on May 21, 2026
  3. changed the title [-][Sentry: APP-8Z0 / APP-8PM] HybridApp iOS EXC_BAD_ACCESS in FullStory native onSessionStarted (~4.6k users)[/-] [+][Sentry: APP-8Z0 / APP-8PM] HybridApp iOS EXC_BAD_ACCESS in FullStory native onSessionStarted [/+] on May 21, 2026
  4. MelvinBot commented on May 21, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    The crash occurs in the FullStory native iOS SDK (v1.68.3) during the session-start callback. When FullStory starts a session, fullstoryDidStartSession calls emitOnSessionStarted (a codegen-generated TurboModule EventEmitter method under New Architecture). Inside that emitter, std::__1::__hash_table::__emplace_unique_key_args hits KERN_INVALID_ADDRESS — a use-after-free or uninitialized memory access in the event emitter's internal data structure.

    The app has New Architecture enabled (Podfile:82), and the emitOnSessionStarted path is only compiled under RCT_NEW_ARCH_ENABLED. The native iOS SDK v1.68.3 is 3 minor versions behind the latest (1.70.1), and FullStory's release notes for v1.70.0 list two directly relevant fixes:

    • "Fixed a rare crash that could occur when starting a session"
    • "Fixed a rare crash associated with objc_retain"

    Both match the crash signature (EXC_BAD_ACCESS during session start, memory access violation).

    What changes do you think we should make in order to solve the problem?

    Upgrade the FullStory native iOS SDK from 1.68.3 to 1.70.1 in ios/Podfile:

    - pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.68.3-xcframework.tar.gz'
    + pod 'FullStory', :http => 'https://ios-releases.fullstory.com/fullstory-1.70.1-xcframework.tar.gz'
    

    Then re-run pod install to update Podfile.lock.

    What alternative solutions did you explore? (Optional)

    1. Wrapping emitOnSessionStarted in a try/catch or dispatch to main queue — this would require patching @fullstory/react-native source. The crash is inside FullStory's own native code, so an SDK upgrade is the cleaner path.
    2. Gating FullStory init on affected iOS versions — would reduce crash rate but leave the underlying bug in place and degrade FullStory coverage.
    Investigation details

    Data flow to crash:

    1. App starts → FullStory native module sets FS.delegate = self during init
    2. FullstoryInitHandler mounts → calls consentAndIdentify() → FullStory.restart()
    3. FullStory SDK starts a session → fires fullstoryDidStartSession delegate callback
    4. Callback calls emitOnSessionStarted (New Arch EventEmitter codegen) outside the @synchronized block
    5. CRASH: __hash_table::__emplace_unique_key_args → KERN_INVALID_ADDRESS

    Key files:

    Impact context: ~4,625 users affected (74 in last 14 days), iOS 26.x dominant, ongoing since 2026-03-27.


    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.

  5. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 21, 2026
  6. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on May 21, 2026
  7. melvin-bot commented on May 21, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @Eskalifer1 (External)

  8. changed the title [-][Sentry: APP-8Z0 / APP-8PM] HybridApp iOS EXC_BAD_ACCESS in FullStory native onSessionStarted [/-] [+][$250] [Sentry: APP-8Z0 / APP-8PM] HybridApp iOS EXC_BAD_ACCESS in FullStory native onSessionStarted [/+] on May 21, 2026
  9. melvin-bot commented on May 21, 2026

    @melvin-bot
  10. KJ21-ENG commented on May 21, 2026

    @KJ21-ENG
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    Hybrid iOS builds can crash on the main thread when FullStory starts a native recording session. The crash is affecting production users and the Sentry stack is inside FullStory's session-start callback / React Native session-start event path.

    What is the root cause of that problem?

    App pins the FullStory native iOS SDK directly in ios/Podfile to https://ios-releases.fullstory.com/fullstory-1.68.3-xcframework.tar.gz, and ios/Podfile.lock confirms the shipped pod is FullStory (1.68.3). Newer FullStory iOS xcframework releases are already available publicly up through 1.70.1, but the app will keep shipping 1.68.3 until that URL and lockfile are updated.

    On native, FullstoryInitHandler calls FS.init(userMetadata), which reaches FullStory.restart() once production metadata is available. That is the same session-start path shown in Sentry. Since the crash stack is below our JS/App code in FullStory's native callback dispatch, App-side try/catch or null checks around FS.init will not catch it; we need to move the native FullStory binary off the affected build.

    What changes do you think we should make in order to solve the problem?

    Update the iOS FullStory pod to the latest available FullStory xcframework release. Concretely, change the direct FullStory pod URL in ios/Podfile from fullstory-1.68.3-xcframework.tar.gz to the latest available release, currently fullstory-1.70.1-xcframework.tar.gz, then regenerate ios/Podfile.lock so the pod version, checkout URL, checksum, and lock metadata all match the new binary. Keep @fullstory/react-native at 1.9.0 unless FullStory publishes a newer React Native wrapper, because npm currently reports 1.9.0 as the latest wrapper version.

    If Sentry still shows the same crash after the SDK bump, add a temporary native-only guard in src/libs/Fullstory/index.native.ts / shouldInitializeFullstory to skip FullStory.restart() for the affected iOS version/device cohort while FullStory investigates. That should be treated as a fallback mitigation, not the primary fix, because it disables recording for a subset of users.

    Scope boundaries:

    • Files touched: ios/Podfile - pod 'FullStory' URL; ios/Podfile.lock - regenerated FullStory pod version/source/checksum entries.
    • Optional fallback only if the SDK bump does not clear Sentry: src/libs/Fullstory/index.native.ts / src/libs/Fullstory/common.ts - native FullStory initialization gate before FullStory.restart().
    • Platforms: iOS native / HybridApp only.
    • Code paths not touched: web FullStory (src/libs/Fullstory/index.ts), Android FullStory Gradle plugin, unrelated telemetry integrations, backend/API contracts.
    • API contracts or backend behavior changes: no.

    What alternative solutions did you explore? (Optional)

    Only wrapping FS.init, FullStory.restart(), or getSessionURL() in additional JS error handling would not address this crash because the fatal fault occurs later inside the native FullStory SDK callback path. Permanently disabling FullStory on iOS would stop the crash, but it is broader than needed and loses session replay for all iOS users; it should only be used as a short-term cohort-specific mitigation if the SDK upgrade is not sufficient.

  11. yusufdeveloper2903 commented on May 21, 2026

    @yusufdeveloper2903
    Contributor

    Proposal

    What is the root cause of that problem?

    The crashing frames -[FullStory fullstoryDidStartSession:] → -[NativeFullStorySpecBase emitOnSessionStarted:] are not in the native FullStory xcframework — they are in the @fullstory/react-native bridge module, which is already on the latest npm release (1.9.0).

    The module registers itself as FullStory's delegate exactly once, in -init, and never clears it — there is no dealloc or invalidate anywhere in the implementation:

    - (instancetype)init {
        self = [super init];
        if (self) {
            FS.delegate = self;   // set once, never reset
            ...

    FullStory.mm L16-L23

    FS.delegate is a class property declared without weak (FullStory.framework/Headers/FS.h):

    @property (class) id<FSDelegate> _Nullable delegate;

    So it is not a zeroing weak reference — when the module instance is freed, FS keeps pointing at the dead object. Under New Architecture, the session-start callback then invokes the codegen event emitter:

    - (void) fullstoryDidStartSession:(NSString *)sessionUrl {
        ...
    #ifdef RCT_NEW_ARCH_ENABLED
        [self emitOnSessionStarted:@{ ... }];   // std::function bound to this module's runtime/CallInvoker
    #endif
    }

    FullStory.mm L197-L209

    The FS SDK is an app-lifetime singleton, but in HybridApp the React Native instance is created and destroyed across OldDot↔NewDot transitions. When FS starts a session after the RN instance that owned this module has been torn down, the never-cleared, non-weak FS.delegate fires fullstoryDidStartSession: into a module whose runtime / emitter storage is already freed. The event emitter's std::function then dereferences freed memory inside __hash_table::__emplace_unique_key_args → KERN_INVALID_ADDRESS (EXC_BAD_ACCESS) — exactly the reported stack. This is why the crash is hybrid_app-only: a standalone NewDot app has one long-lived RN instance, so the dangling-delegate window barely exists.

    What changes do you think we should make in order to solve the problem?

    Stop a torn-down module from receiving the native callback by clearing the delegate when it goes away — patching @fullstory/react-native/ios/FullStory.mm via patch-package (new file patches/@fullstory+react-native+1.9.0.patch):

      - (instancetype)init {
          self = [super init];
          if (self) {
              FS.delegate = self;
              onReadyPromises = [NSMutableArray new];
          }
          return self;
      }
    +
    + - (void)dealloc {
    +     // FS.delegate is a non-weak class property on an app-lifetime singleton.
    +     // Clear it when this RN module is freed so a session-started callback can't
    +     // fire into a torn-down runtime (HybridApp OldDot<->NewDot transitions).
    +     if (FS.delegate == self) {
    +         FS.delegate = nil;
    +     }
    + }

    The if (FS.delegate == self) guard is important: when a new RN instance spins up it overwrites FS.delegate in its own -init, so a late-deallocating old instance must not clobber the live registration.

    For extra determinism I'd also conform the module to RCTInvalidating and run the same clear in -invalidate, which fires on RN-instance teardown before dealloc.

    This lives in the JS package, so it is picked up by every iOS build that consumes @fullstory/react-native (including the HybridApp build that bundles the App's node_modules), and it doesn't depend on a third-party release-note guess.

    What alternative solutions did you explore? (Optional)

    1. Upgrade the native FullStory xcframework (ios/Podfile L126, currently 1.68.3). FullStory 1.70.0 release notes do mention a session-start crash fix, so this may help as a complementary measure. I rejected it as the primary fix because (a) it gambles that the release-note line covers this exact New-Arch emit path — the crashing code is in the RN bridge, which the xcframework bump doesn't touch; and (b) the crash is on HybridApp builds, whose iOS binary is built from the Mobile-Expensify submodule, not App's ios/ project — so a App/ios/Podfile-only bump updates just the standalone NewDot iOS dev build and would not ship to the crashing builds unless the same pin is updated wherever HybridApp resolves FullStory.
    2. Gate FullStory init on the affected OS/device subset — lowers the crash rate but leaves the dangling delegate in place and degrades FullStory coverage. Rejected.
  12. 32 remaining items

  13. melvin-bot commented on Jun 11, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  14. melvin-bot commented on Jun 11, 2026

    @melvin-bot

    Payment Summary

    Upwork Job

    • Contributor: @fabioh8010 does not require payment (Contractor)
    • ROLE: @Eskalifer1 paid $(AMOUNT) via Upwork (LINK)

    BugZero Checklist (@mallenexpensify)

    • I have verified the correct assignees and roles are listed above and updated the necessary manual offers
    • I have verified that there are no duplicate or incorrect contracts on Upwork for this job (https://www.upwork.com/ab/applicants/2057428000347458847/hired)
    • I have verified the PR was not reverted
    • I have applied any discounts due to bugs/regressions introduced by this PR
    • I have paid out the Upwork contracts or cancelled the ones that are incorrect
    • I have verified the payment summary above is correct
  15. Eskalifer1 commented on Jun 15, 2026

    @Eskalifer1
    Contributor

    BugZero Checklist:

    • [Contributor] The offending PR and associated issue have been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      N/A - we bumped libs versions here

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: N/A

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

      N/A - we bumped libs versions here

  16. mallenexpensify commented on Jun 15, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @Eskalifer1 paid $250 via Upwork.

    @Eskalifer1 can you please accept the job below? Please reply here and tag me once you have.
    If you're not sent an offer in Upwork when you're assigned to an issue, please tag me. If an Upwork job exists, please submit a proposal.
    https://www.upwork.com/jobs/~022057428000347458847

  17. Eskalifer1 commented on Jun 15, 2026

    @Eskalifer1
    Contributor

    Hi @mallenexpensify, sure, i have accepted, thank you!

  18. mallenexpensify commented on Jun 15, 2026

    @mallenexpensify
    Contributor

    @Eskalifer1 paid, summary updated above. Thx.

  19. moved this from Done to SUBISSUE in [#whatsnext] #qualityon Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions