Summary
ComposeMaskingOptionsTest > when sentry-unmask modifier is set unmasks the node fails intermittently on main with:
java.lang.AssertionError: Node with text null should be masked
at io.sentry.android.replay.viewhierarchy.ComposeMaskingOptionsTest.kt:238
(:sentry-android-replay:testReleaseUnitTest, Java 17 build job.)
Evidence it's flaky, not a regression
- Same test, same assertion failed on two unrelated commits while commits in between passed:
- Both failures are preceded by a JVM instrumentation-agent error in the same run:
*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" ... transform method call failed at .../JPLISAgent.c line: 876
Likely mechanism
In ComposeViewHierarchyNode.fromComposeNode (ComposeViewHierarchyNode.kt:178-189), masking is gated on visibility:
val isVisible =
!SentryLayoutNodeHelper.isTransparent(node) &&
(semantics == null || !semantics.contains(InvisibleToUser)) &&
visibleRect.height > 0 && visibleRect.width > 0
...
val shouldMask = isVisible && semantics.shouldMask(isImage = false, options)
visibleRect comes from node.coordinates.boundsInWindow(...). If a node's bounds resolve to empty (width/height == 0), isVisible is false and the node is not masked even with maskAllText = true — exactly the failing assertion (the activity-title node, whose layout isn't a ComposeTextLayout, hence text == null).
Under Robolectric, Compose layout/coordinate resolution after shadowOf(Looper.getMainLooper()).idle() is timing-sensitive; when layout hasn't fully settled (occasionally perturbed by the instrumentation-agent transform failures above), boundsInWindow returns empty bounds → isVisible = false → node not masked → intermittent failure.
Possible fixes
- Await/assert non-empty bounds for the laid-out nodes before running the masking assertions.
- Make the masking assertions robust to the non-Compose activity-title node.
- Investigate the
java.lang.instrument transform failure, which may be aggravating layout timing.
Unrelated to the MediaMuxer leak tracked in #5584 / PR #5583.
Summary
ComposeMaskingOptionsTest > when sentry-unmask modifier is set unmasks the nodefails intermittently onmainwith:(
:sentry-android-replay:testReleaseUnitTest, Java 17 build job.)Evidence it's flaky, not a regression
0c118e90— https://github.com/getsentry/sentry-java/actions/runs/27941528687/job/82675843497f6192aac— https://github.com/getsentry/sentry-java/actions/runs/27750608535*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" ... transform method call failed at .../JPLISAgent.c line: 876Likely mechanism
In
ComposeViewHierarchyNode.fromComposeNode(ComposeViewHierarchyNode.kt:178-189), masking is gated on visibility:visibleRectcomes fromnode.coordinates.boundsInWindow(...). If a node's bounds resolve to empty (width/height == 0),isVisibleis false and the node is not masked even withmaskAllText = true— exactly the failing assertion (the activity-title node, whose layout isn't aComposeTextLayout, hencetext == null).Under Robolectric, Compose layout/coordinate resolution after
shadowOf(Looper.getMainLooper()).idle()is timing-sensitive; when layout hasn't fully settled (occasionally perturbed by the instrumentation-agent transform failures above),boundsInWindowreturns empty bounds →isVisible = false→ node not masked → intermittent failure.Possible fixes
java.lang.instrumenttransform failure, which may be aggravating layout timing.Unrelated to the MediaMuxer leak tracked in #5584 / PR #5583.