Skip to content

[Sentry: APP-KDF] HybridApp iOS fatal hang — RCTMarkdownUtils applyMarkdownFormatting blocks the main thread in the Yoga measure pass #99849

Description

@mountiny

Sentry

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

Impact (snapshot at filing)

  • Users (total since first seen): 37
  • Events: 42
  • Users (last 14d): 37
  • First seen: 2026-08-24 (Sentry substatus is still new)
  • Last seen: ongoing
  • Platform: iOS HybridApp
  • App version(s): 9.4.56-3, 9.4.57-3, 9.4.58-4, 9.4.59-5, 9.4.60-2
  • Mechanism: AppHang (Fatal App Hang Fully Blocked)
  • Culprit: -[RCTMarkdownUtils applyMarkdownFormatting:withDefaultTextAttributes:markdownStyle:parserId:]

Every affected user in the 14 day window is a new user of this signature, so this is a fresh
regression rather than a long tail.

Stack trace (top frames, main thread)

__ulock_wait2
_os_unfair_lock_lock_slow
objc_sync_enter
-[RCTMarkdownUtils applyMarkdownFormatting:withDefaultTextAttributes:markdownStyle:parserId:]
facebook::react::MarkdownTextInputDecoratorShadowNode::applyMarkdownFormattingToTextInputState
facebook::react::MarkdownTextInputDecoratorShadowNode::measureContent
facebook::react::MarkdownTextInputDecoratorShadowNode::yogaNodeMeasureCallbackConnector
facebook::yoga::Node::measure (Node.cpp:59)
facebook::yoga::measureNodeWithMeasureFunc (CalculateLayout.cpp:356)
facebook::yoga::calculateLayoutImpl (CalculateLayout.cpp:1349)
... ~100 further Yoga frames: calculateLayoutInternal / calculateLayoutImpl /
    computeFlexBasisForChild / resolveFlexibleLength / layoutAbsoluteDescendants ...
facebook::react::YogaLayoutableShadowNode::layoutTree (YogaLayoutableShadowNode.cpp:683)

The event also shows LiveMarkdownRuntime_queue and LiveMarkdownRuntime(timeout) threads alive
while the main thread sits in the lock.

Suspected cause

The Yoga measure callback for MarkdownTextInputDecoratorShadowNode calls
applyMarkdownFormatting synchronously on the main thread, and that method blocks on the
@synchronized lock inside RCTMarkdownUtils. The stack shows a very deep layout pass, roughly 100
Yoga frames with repeated layoutAbsoluteDescendants recursion, so the measure function is invoked
many times in one pass while the parser lock is held elsewhere. The main thread waits past the two
second watchdog threshold and iOS terminates the app.

Two things worth checking:

  1. Whether applyMarkdownFormatting can be made lock free on the measure path, or given a cached
    measurement so repeated measures of unchanged text do not re-enter the parser.
  2. Why the layout tree is this deep. The repeated absolute-position recursion suggests a markdown
    input nested inside several absolutely positioned containers, which multiplies measure calls.

Reproduction

Unknown — see the events linked from the Sentry issue. The affected sample includes iPad on iOS 26.6,
so a large composer with long markdown content is a plausible starting point.

Related

Issue OwnerCurrent Issue Owner: @Krishna2323

Activity

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