Skip to content

fix(replay): Fix VerifyError in Compose masking under DexGuard/R8 obfuscation#5507

Merged
romtsn merged 4 commits into
mainfrom
rz/fix/compose-replay-dexguard-verifyerror
Jun 8, 2026
Merged

fix(replay): Fix VerifyError in Compose masking under DexGuard/R8 obfuscation#5507
romtsn merged 4 commits into
mainfrom
rz/fix/compose-replay-dexguard-verifyerror

Conversation

@romtsn
Copy link
Copy Markdown
Member

@romtsn romtsn commented Jun 8, 2026

📜 Description

ComposeViewHierarchyNode traversal computed node bounds via LayoutCoordinates.boundsInWindow(...), which returned an android.graphics.Rect while the surrounding code treated the value as androidx.compose.ui.geometry.Rect — mixing the two Rect types within fromComposeNode(...).

Under aggressive obfuscation/optimization (DexGuard 9.13.2 / R8 full mode) this could be rejected at class verification time with a VerifyError ('Unresolved Reference: androidx.compose.ui.geometry.Rect' not instance of ...), crashing Session Replay the moment it traversed the first Compose screen.

Fix:

  • boundsInWindow(...) now consistently returns androidx.compose.ui.geometry.Rect (float-based), so a single Rect type flows through the method.
  • Added an internal fun Rect.toRect(): android.graphics.Rect extension to convert only at the boundary where the view-hierarchy node actually needs android.graphics.Rect.

💡 Motivation and Context

Fixes #5497 — Session Replay VerifyError in ComposeViewHierarchyNode under DexGuard/R8 obfuscation.

💚 How did you test it?

Existing Session Replay Compose unit tests; built the replay module in release. The crash itself only reproduces under aggressive obfuscation (DexGuard / R8 full mode), where the mixed Rect types tripped the verifier; collapsing to a single Rect type removes the ambiguity.

Example replay: https://sentry-sdks.sentry.io/explore/replays/6783b073aef442a8b4ad8731bf6901fa

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

🔮 Next steps

romtsn and others added 2 commits June 8, 2026 10:18
…uscation

ComposeViewHierarchyNode.boundsInWindow returned an android.graphics.Rect
while the surrounding code carried it as androidx.compose.ui.geometry.Rect,
mixing the two Rect types in the same method. Under aggressive obfuscation
(DexGuard 9.13.2 / R8 full mode) this could be rejected at class load with
a VerifyError, crashing Replay when traversing the Compose tree.

Make boundsInWindow return androidx.compose.ui.geometry.Rect throughout and
add a Rect.toRect() extension to convert to android.graphics.Rect only at
the boundary where the view-hierarchy node needs it.

Fixes #5497

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sentry
Copy link
Copy Markdown

sentry Bot commented Jun 8, 2026

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.43.1 (1) release

⚙️ sentry-android Build Distribution Settings

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 8, 2026

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 342.26 ms 428.04 ms 85.79 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
2387c2c 317.04 ms 354.60 ms 37.56 ms
91bb874 310.68 ms 359.24 ms 48.56 ms
cf708bd 408.35 ms 458.98 ms 50.63 ms
2195398 321.31 ms 391.66 ms 70.35 ms
e2dce0b 315.85 ms 369.20 ms 53.35 ms
991b33b 326.08 ms 397.82 ms 71.73 ms
4c04bb8 350.71 ms 413.63 ms 62.92 ms
3699cd5 423.60 ms 495.52 ms 71.92 ms
70118e9 380.00 ms 475.72 ms 95.72 ms
694d587 312.37 ms 402.77 ms 90.41 ms

App size

Revision Plain With Sentry Diff
2387c2c 1.58 MiB 2.13 MiB 559.54 KiB
91bb874 1.58 MiB 2.13 MiB 559.07 KiB
cf708bd 1.58 MiB 2.11 MiB 539.71 KiB
2195398 0 B 0 B 0 B
e2dce0b 0 B 0 B 0 B
991b33b 0 B 0 B 0 B
4c04bb8 0 B 0 B 0 B
3699cd5 1.58 MiB 2.10 MiB 533.45 KiB
70118e9 1.58 MiB 2.29 MiB 719.84 KiB
694d587 1.58 MiB 2.19 MiB 620.06 KiB

Previous results on branch: rz/fix/compose-replay-dexguard-verifyerror

Startup times

Revision Plain With Sentry Diff
ac09909 451.94 ms 565.65 ms 113.71 ms
1864c74 311.33 ms 381.27 ms 69.93 ms

App size

Revision Plain With Sentry Diff
ac09909 0 B 0 B 0 B
1864c74 0 B 0 B 0 B

@romtsn romtsn marked this pull request as ready for review June 8, 2026 08:38
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e56c863. Configure here.

isVisible/shouldMask are derived from the sub-pixel float bounds, but the
android.graphics.Rect stored on the node (and drawn by MaskRenderer) used
truncating toInt(). A sub-pixel node could be marked visible+maskable yet
store a zero-width/height rect, so the mask wasn't drawn and sensitive
content leaked. Round outward (floor min, ceil max) so a non-empty float
rect always yields a non-empty integer rect, biasing toward over-masking.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@runningcode runningcode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@romtsn romtsn enabled auto-merge (squash) June 8, 2026 15:40
@romtsn romtsn merged commit 8c7718c into main Jun 8, 2026
68 checks passed
@romtsn romtsn deleted the rz/fix/compose-replay-dexguard-verifyerror branch June 8, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session Replay: VerifyError in ComposeViewHierarchyNode (androidx.compose.ui.geometry.Rect) under DexGuard/R8 obfuscation

2 participants