Skip to content

fix(replay): Accept Double timestamps in Android network breadcrumb conversion - #3859

Merged
buenaflor merged 1 commit into
getsentry:mainfrom
aqrc:fix/android-replay-network-breadcrumb-double-timestamps
Jul 6, 2026
Merged

buenaflor merged 1 commit into
getsentry:mainfrom
aqrc:fix/android-replay-network-breadcrumb-double-timestamps

Conversation

@aqrc

@aqrc aqrc commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

📜 Description

Fixes Session Replay being broken on Android since 9.21.0: replays were either missing entirely or uploaded as short glitchy fragments.

SentryFlutterReplayBreadcrumbConverter.convertNetworkBreadcrumb cast start_timestamp / end_timestamp with an unchecked as Long. Since #3708, breadcrumbs are synced to Android as JSON bytes and re-parsed by sentry-java's Breadcrumb.Deserializer, whose JsonObjectDeserializer.nextNumber() tries nextInt() → nextDouble() → nextLong(). Epoch-millisecond values overflow Integer, so they are parsed as Double and nextLong() is never reached. The cast then threw for every http breadcrumb:

E/Sentry: Failed to execute task SessionCaptureStrategy.add_frame
E/Sentry: java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.Long
	at io.sentry.flutter.SentryFlutterReplayBreadcrumbConverter.convertNetworkBreadcrumb(SentryFlutterReplayBreadcrumbConverter.kt:84)
	at io.sentry.flutter.SentryFlutterReplayBreadcrumbConverter.convert(SentryFlutterReplayBreadcrumbConverter.kt:30)
	at io.sentry.android.replay.capture.CaptureStrategy$Companion.buildReplay(CaptureStrategy.kt:200)
	at io.sentry.android.replay.capture.CaptureStrategy$Companion.createSegment(CaptureStrategy.kt:113)

Because the exception is thrown inside replay segment creation, any segment containing an http breadcrumb was dropped — in apps with regular network traffic this effectively disables Session Replay on Android.

This change accepts any Number for the timestamps and returns null when url or the timestamps are missing or mistyped instead of throwing, matching what the iOS converter (convertNetwork: in SentryFlutterReplayBreadcrumbConverter.m) already does with its isKindOfClass: guards.

💡 Motivation and Context

Replay worked on 9.20.0 (typed method-channel scope sync preserved Long) and broke on 9.21.0+ for Android. The same numeric-subtype issue was fixed in sentry-react-native's converter in getsentry/sentry-react-native#6288.

💚 How did you test it?

  • Reproduced the ClassCastException against the unmodified converter by invoking convert() with an http breadcrumb whose start_timestamp/end_timestamp are Double (compiled against sentry-android 8.47.0).
  • Verified the fixed converter with the same harness:
    • Double timestamps → RRWebSpanEvent with correct second-precision start/end timestamps
    • Long timestamps (pre-9.21 path) → unchanged behavior
    • missing url or non-numeric timestamp → returns null instead of throwing
  • ktlint passes.
  • Reproduced the problem in the app, then verified that it disappeared with this fix.

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed
  • All tests passing
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec
  • No breaking changes

🔮 Next steps

The root cause — sentry-java's JsonObjectDeserializer.nextNumber() returning Double for any value that overflows Integer — may affect other consumers of JSON-synced scope data and could be worth fixing upstream in sentry-java as well.

🤖 Generated with Claude Code

…onversion

Since 9.21.0 breadcrumbs are synced to Android as JSON bytes and re-parsed
by sentry-java, whose JsonObjectDeserializer parses epoch-millisecond values
as Double (they overflow Integer, and nextDouble() is tried before
nextLong()). The unchecked 'as Long' cast in convertNetworkBreadcrumb then
threw ClassCastException for every http breadcrumb, failing replay segment
creation and breaking Session Replay on Android.

Accept any Number for start/end_timestamp and skip the event when fields
are missing or mistyped, matching the iOS converter behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@aqrc
aqrc requested review from buenaflor and denrase as code owners July 6, 2026 13:42
@codecov

codecov Bot commented Jul 6, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.50%. Comparing base (c652e19) to head (ad5ffa3).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3859      +/-   ##
==========================================
+ Coverage   87.35%   91.50%   +4.15%     
==========================================
  Files         338      105     -233     
  Lines       12283     3803    -8480     
==========================================
- Hits        10730     3480    -7250     
+ Misses       1553      323    -1230     
Flag Coverage Δ
sentry ?
sentry_dio ?
sentry_drift ?
sentry_file ?
sentry_firebase_remote_config 100.00% <ø> (ø)
sentry_flutter 91.10% <ø> (ø)
sentry_grpc ?
sentry_hive ?
sentry_isar ?
sentry_link ?
sentry_logging ?
sentry_sqflite ?
sentry_supabase 97.27% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@aqrc

aqrc commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

hey, sorry in advance that this PR is not impeccable, I've been looking into this problem with the help of Claude then asked if he "can fix it and create a PR" theoretically, but he actually went full throttle 😬 The problem is real and the fix should be working too, but I am going to manually review and test everything now

@buenaflor

Copy link
Copy Markdown
Contributor

@aqrc thx! ignore the CI failures, the e2e test is not running due to missing auth permissions since it's a fork

@aqrc

aqrc commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I reviewed the code - looks legit to me, only questionable thing is the comment (it is up to you to keep it or to remove it).

I also imported the fix into a real app and I confirm that Replay works correctly now.

@buenaflor buenaflor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thx, lgtm 👍

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.

2 participants