Skip to content

[$250] [Sentry: APP-MDA] FragmentManager race in legacy OldDot navigation (yapl) #102742

Open

Description

@mountiny

Sentry

https://expensify.sentry.io/issues/APP-MDA

Impact (snapshot at filing)

  • Users (total since first seen): 67
  • Events: 76
  • Users (last 14d): not pulled separately
  • First seen: 2026-08-26
  • Last seen: now (ongoing)
  • Platform: Android (HybridApp, legacy OldDot navigation layer)
  • App version(s): current HybridApp Android production builds
  • Affected route(s) (parameterized): n/a — native OldDot navigation stack, not a JS route
  • Mechanism: signalhandler (SIGABRT, fatal)

Stack trace (top frames, first-party only)

androidx.fragment.app.FragmentManager.ensureExecReady
androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss
yapl.android.managers.navigation.PageNavigator.popLastPage
yapl.android.managers.navigation.NavigationManager.dismissModal / popLastPage
yapl.android.api.calls.yaplNavigationManager.handleCommand

Error: IllegalStateException: FragmentManager is already executing transactions

Suspected cause

Legacy OldDot (yapl) NavigationManager calls commitNowAllowingStateLoss while FragmentManager is already executing another transaction. Likely two navigation commands (dismissModal and popLastPage) firing back-to-back without waiting for the first to finish.

Reproduction

Unknown — see Sentry events linked from the Sentry issue.

Related

  • Sibling Sentry IDs: none identified
  • Prior GH issues: none found
Upwork Automation - Do Not Edit
Issue OwnerCurrent Issue Owner: @ZhenjaHorbach

Activity

  1. self-assigned this
    on Oct 1, 2026
  2. added
    ExternalAdded to denote the issue can be worked on by a contributor
    BugSomething is broken. Auto assigns a BugZero manager.
    on Oct 1, 2026
  3. melvin-bot commented on Oct 1, 2026

    @melvin-bot

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

  4. changed the title [-][Sentry: APP-MDA] FragmentManager race in legacy OldDot navigation (yapl)[/-] [+][$175] [Sentry: APP-MDA] FragmentManager race in legacy OldDot navigation (yapl)[/+] on Oct 1, 2026
  5. melvin-bot commented on Oct 1, 2026

    @melvin-bot
  6. MelvinBot commented on Oct 1, 2026

    @MelvinBot
    Contributor

    Issue Analysis

    This appears to be a backend/internal issue. A separate internal investigation is needed.

    Why: Every first-party frame in the stack trace is in the legacy OldDot native navigation layer. That code is not in the App repo, so contributors cannot fix it here. I found no App code path that sends these navigation commands.

    Next step: An internal engineer with access to that code should take this. The External label and Upwork job likely don't fit, since contributors can't reach the code.

    I found no duplicate open issues.


    view run

  7. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Oct 1, 2026
  8. changed the title [-][$175] [Sentry: APP-MDA] FragmentManager race in legacy OldDot navigation (yapl)[/-] [+][$250] [Sentry: APP-MDA] FragmentManager race in legacy OldDot navigation (yapl)[/+] on Oct 1, 2026
  9. Dip-G-Git commented on Oct 1, 2026

    @Dip-G-Git
  10. github-actions commented on Oct 1, 2026

    @github-actions
    Contributor

    🤖 ProposalPolice™ is tracking duplicate proposals for this issue using an OpenAI Conversation. This comment stores that Conversation's ID and can be safely ignored.

  11. 44 remaining items

  12. melvin-bot commented on Oct 7, 2026

    @melvin-bot

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

  13. mountiny commented on Oct 7, 2026

    @mountiny
    ContributorAuthor

    @ZhenjaHorbach can you please take a look into this one? Thank you.

  14. ZhenjaHorbach commented on Oct 7, 2026

    @ZhenjaHorbach
    Contributor

    @ZhenjaHorbach can you please take a look into this one? Thank you.

    Will check tomorrow morning!

  15. amar-sheikh commented on Oct 7, 2026

    @amar-sheikh

    @ZhenjaHorbach Thanks for picking this up! When you get to it, could you take a look at my proposal? It's grounded in the AndroidX FragmentManager source directly and keeps the fix self-contained to this repo.

  16. ZhenjaHorbach commented on Oct 8, 2026

    @ZhenjaHorbach
    Contributor

    @mountiny
    Just worried about this issue
    Should we find somebody from the agencies, since they have access to OldDot?

  17. mountiny commented on Oct 9, 2026

    @mountiny
    ContributorAuthor

    Can you post in the expert contributors channel to look for volunteers? You can tag Marcin

  18. melvin-bot commented on Oct 13, 2026

    @melvin-bot

    @ZhenjaHorbach Huh... This is 4 days overdue. Who can take care of this?

  19. ZhenjaHorbach commented on Oct 14, 2026

    @ZhenjaHorbach
    Contributor

    @suneox
    I think you can start working on this in any case!

  20. suneox commented on Oct 15, 2026

    @suneox
    Contributor

    Root cause and fix plan from slack

    Root cause

    OldDot navigation on Android (PageNavigator) commits every page change synchronously with commitNowAllowingStateLoss() (popLastPage, popAllPages, pushPage). When a navigation (e.g. popLastPage triggered by a network response) runs while the previous page's exit animation is still in progress, AndroidX cancels that animation and destroys the page inside the running transaction. BaseViewController.onDestroy calls into JS, and the page's JS onDestroy navigates again synchronously: FixExpenseViolationsController.onDestroy -> endViolationFixingFlow -> NavigationManager.dismissModal. So a second commit starts while the first one is still executing -> IllegalStateException: FragmentManager is already executing transactions. The exception is thrown inside a JNI callback (yaplNavigationManager.handleCommand), so JSC aborts the app (SIGABRT). Sentry also shows the same nested pattern from YaplActivityBase.onResume -> yaplOpenNewApp -> popAllPages.

    Solution

    Make PageNavigator re-entrancy safe instead of patching each JS onDestroy. It tracks when a fragment commit is in progress, and any navigation command (push/pop/dismiss) that arrives during that time is deferred to the next main-loop tick instead of being committed in a nested way. Normal navigation is unchanged; only the case that crashes today gets delayed by one tick. This covers both crash patterns in Sentry and any other page whose onDestroy/lifecycle callback navigates.

    @MelvinBot validate this approach

  21. MelvinBot commented on Oct 15, 2026

    @MelvinBot
    Contributor

    Verdict: The approach is sound for the crash in the stack trace. Add a fallback, though, because a navigator-owned flag won't catch every nested commit.

    I couldn't read the native code or Sentry from here, so I checked the plan against the public AndroidX source only.

    What AndroidX confirms

    Gaps to cover

    1. AndroidX also sets the executing state during lifecycle dispatch, such as pause, stop and destroyView (#L3292-L3305). Your flag won't see those. Catch this IllegalStateException around the commit and post the command as a fallback.
    2. Once a command is queued, queue every later command too, and drain in order. Otherwise a newer command can run before an older deferred one.
    3. Make deferred pop and dismiss idempotent. The deferred dismiss runs after the outer pop, so it could remove one page too many.
    4. Defer only commands that return nothing to JS.
    5. Clear the queue when the activity is destroyed. AndroidX silently drops these commits during teardown (#L1951-L1955).

    To reproduce: Set Animator duration scale to 10x in Developer options. Then trigger the second navigation while the exit animation runs.


    view run

  22. ZhenjaHorbach commented on Oct 15, 2026

    @ZhenjaHorbach
    Contributor

    This proposal looks good to me!

    🎀👀🎀 С+

  23. melvin-bot commented on Oct 15, 2026

    @melvin-bot

    Current assignee @mountiny is eligible for the choreEngineerContributorManagement assigner, not assigning anyone new.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

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