Skip to content

feat(replay): Add Session Replay runtime controls - #6703

Merged
antonis merged 9 commits into
mainfrom
feat/replay-runtime-controls
Sep 14, 2026
Merged

antonis merged 9 commits into
mainfrom
feat/replay-runtime-controls

Conversation

@antonis

@antonis antonis commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • New feature
  • Bugfix
  • Enhancement
  • Refactoring

📜 Description

Adds JS-accessible Session Replay runtime controls so recording can be started, stopped, and managed manually at runtime, with the same API on every platform. The controls are reached through a shared Replay interface via a new Sentry.getReplay():

const replay = Sentry.getReplay();

replay?.start();          // start a replay regardless of sampling
replay?.startBuffering(); // buffer until flush() or an error
await replay?.stop();
replay?.pause();
replay?.resume();
await replay?.flush();
replay?.getReplayId();
  • Mobile — each control forwards across the bridge to the native SDKs: Sentry.replay().* on Android (sentry-java) and SentrySDK.internal.replay.* on iOS (sentry-cocoa). Wired on both architectures (TurboModule/New via codegen NativeRNSentry.ts, and the legacy bridge) and both platforms.
  • Web — delegates to the upstream browser Session Replay integration. pause()/resume() are not exposed by the browser SDK, so they degrade to a logged no-op to keep the cross-platform API interchangeable.

The shared Replay interface mirrors the native Session Replay API shape across platforms.

💡 Motivation and Context

💚 How did you test it?

📝 Checklist

🔮 Next steps

antonis added a commit that referenced this pull request Sep 11, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • feat(replay): Add Session Replay runtime controls by antonis in #6703
  • chore: Deduplicate yarn.lock by antonis in #6706
  • fix(tracing): background root spans overwrite native propagation context by alwx in #6720
  • ci(e2e): Fix broken idb-companion install on iOS e2e by antonis in #6719
  • chore(deps): Bump smol-toml to ^1.7.1 by antonis in #6713
  • chore(deps): Bump js-yaml to patched versions to resolve security alerts by antonis in #6710
  • chore(deps): Bump morgan to ^1.12.0 to resolve security alert by antonis in #6712
  • chore(deps): Bump joi to ^17.13.6 by antonis in #6714
  • chore(deps): Bump sharp to ^0.35.4 to resolve security alert by antonis in #6711
  • chore(deps): bump actions/setup-java from 6.0.0 to 6.0.1 by dependabot in #6716
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.30.1 to 2.31.0 by dependabot in #6718
  • chore(deps): bump getsentry/craft from 2.30.1 to 2.31.0 by dependabot in #6717
  • chore(deps): bump the codeql-action group with 3 updates by dependabot in #6715
  • fix(core): Fix process.exit typo in debugid script by friederbluemle in #6707
  • chore(ci): Only build production builds for visionOS and tvOS by antonis in #6700
  • chore(deps): update Sentry Android Gradle Plugin to v6.22.0 by github-actions in #6699

🤖 This preview updates automatically when you update the PR.

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 24a18f8

@antonis antonis added the ready-to-merge Triggers the full CI test suite label Sep 11, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/src/js/replay/mobilereplay.ts Outdated
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 419.96 ms 430.78 ms 10.82 ms
Size 50.56 MiB 56.49 MiB 5.93 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
37a2091+dirty 407.82 ms 441.22 ms 33.40 ms
f3215d3+dirty 411.11 ms 454.38 ms 43.27 ms
bc8f61e+dirty 442.98 ms 506.56 ms 63.58 ms
f9c1ed4+dirty 431.00 ms 466.22 ms 35.22 ms
7887847+dirty 416.61 ms 462.04 ms 45.43 ms
57e0069+dirty 413.82 ms 453.47 ms 39.65 ms
a0a3177+dirty 441.27 ms 499.86 ms 58.59 ms
09a902f+dirty 427.38 ms 473.80 ms 46.42 ms
a5d243c+dirty 424.52 ms 485.18 ms 60.66 ms
5a23c47+dirty 423.85 ms 466.47 ms 42.62 ms

App size

Revision Plain With Sentry Diff
37a2091+dirty 48.30 MiB 53.58 MiB 5.28 MiB
f3215d3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
bc8f61e+dirty 49.74 MiB 55.09 MiB 5.35 MiB
f9c1ed4+dirty 49.74 MiB 54.86 MiB 5.12 MiB
7887847+dirty 49.74 MiB 54.81 MiB 5.07 MiB
57e0069+dirty 49.74 MiB 54.85 MiB 5.11 MiB
a0a3177+dirty 49.74 MiB 55.37 MiB 5.63 MiB
09a902f+dirty 49.74 MiB 54.81 MiB 5.07 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
5a23c47+dirty 49.74 MiB 54.82 MiB 5.07 MiB

Previous results on branch: feat/replay-runtime-controls

Startup times

Revision Plain With Sentry Diff
e15671a+dirty 424.54 ms 453.72 ms 29.18 ms
b64503c+dirty 431.70 ms 453.77 ms 22.07 ms
1efd073+dirty 416.80 ms 433.18 ms 16.38 ms

App size

Revision Plain With Sentry Diff
e15671a+dirty 50.56 MiB 56.49 MiB 5.93 MiB
b64503c+dirty 50.56 MiB 56.49 MiB 5.93 MiB
1efd073+dirty 50.56 MiB 56.49 MiB 5.93 MiB

@sentry

sentry Bot commented Sep 11, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.26.0 (106) Release

⚙️ sentry-react-native Build Distribution Settings

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3879.36 ms 1238.52 ms -2640.84 ms
Size 5.15 MiB 6.90 MiB 1.75 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
64630e5+dirty 3842.70 ms 1218.11 ms -2624.60 ms
b0d3373+dirty 3831.75 ms 1227.29 ms -2604.46 ms
b04af96+dirty 3818.92 ms 1219.76 ms -2599.16 ms
3d31fcf+dirty 3838.09 ms 1223.46 ms -2614.63 ms
a0a3177+dirty 3844.73 ms 1225.23 ms -2619.51 ms
af33f3b+dirty 3849.98 ms 1236.45 ms -2613.53 ms
09a902f+dirty 3835.67 ms 1217.11 ms -2618.57 ms
5a316ea+dirty 3820.11 ms 1211.28 ms -2608.83 ms
acd838e+dirty 3849.78 ms 1230.00 ms -2619.78 ms
c2e182c+dirty 3848.40 ms 1211.79 ms -2636.61 ms

App size

Revision Plain With Sentry Diff
64630e5+dirty 4.98 MiB 6.46 MiB 1.49 MiB
b0d3373+dirty 5.15 MiB 6.68 MiB 1.53 MiB
b04af96+dirty 4.98 MiB 6.54 MiB 1.56 MiB
3d31fcf+dirty 4.98 MiB 6.56 MiB 1.58 MiB
a0a3177+dirty 4.98 MiB 6.55 MiB 1.58 MiB
af33f3b+dirty 4.98 MiB 6.51 MiB 1.54 MiB
09a902f+dirty 4.98 MiB 6.46 MiB 1.49 MiB
5a316ea+dirty 4.98 MiB 6.51 MiB 1.53 MiB
acd838e+dirty 5.15 MiB 6.70 MiB 1.55 MiB
c2e182c+dirty 4.98 MiB 6.50 MiB 1.52 MiB

Previous results on branch: feat/replay-runtime-controls

Startup times

Revision Plain With Sentry Diff
1efd073+dirty 3875.98 ms 1231.56 ms -2644.41 ms
e15671a+dirty 3877.67 ms 1236.27 ms -2641.41 ms
b64503c+dirty 3857.09 ms 1241.60 ms -2615.49 ms

App size

Revision Plain With Sentry Diff
1efd073+dirty 5.15 MiB 6.90 MiB 1.75 MiB
e15671a+dirty 5.15 MiB 6.90 MiB 1.75 MiB
b64503c+dirty 5.15 MiB 6.90 MiB 1.75 MiB

Comment thread packages/core/etc/sentry-react-native.api.md
Comment thread packages/core/src/js/replay/replayInterface.ts
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3872.60 ms 1234.96 ms -2637.64 ms
Size 5.15 MiB 6.90 MiB 1.75 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
7a89652+dirty 3827.11 ms 1211.49 ms -2615.62 ms
0a147b2+dirty 3845.11 ms 1230.81 ms -2614.30 ms
5b7e8a7+dirty 3855.46 ms 1234.47 ms -2620.99 ms
5fe1c6c+dirty 1201.36 ms 1209.15 ms 7.78 ms
580fb5c+dirty 3835.45 ms 1218.61 ms -2616.84 ms
94af3bd+dirty 3847.84 ms 1230.33 ms -2617.51 ms
f3215d3+dirty 3846.08 ms 1231.85 ms -2614.23 ms
a636fa4+dirty 3846.16 ms 1210.10 ms -2636.05 ms
5a21b51+dirty 3837.87 ms 1223.47 ms -2614.40 ms
bbb8d67+dirty 3847.47 ms 1225.13 ms -2622.34 ms

App size

Revision Plain With Sentry Diff
7a89652+dirty 5.15 MiB 6.70 MiB 1.55 MiB
0a147b2+dirty 4.98 MiB 6.51 MiB 1.53 MiB
5b7e8a7+dirty 5.15 MiB 6.70 MiB 1.54 MiB
5fe1c6c+dirty 3.38 MiB 4.77 MiB 1.39 MiB
580fb5c+dirty 4.98 MiB 6.46 MiB 1.48 MiB
94af3bd+dirty 5.15 MiB 6.69 MiB 1.53 MiB
f3215d3+dirty 5.15 MiB 6.67 MiB 1.52 MiB
a636fa4+dirty 4.98 MiB 6.55 MiB 1.57 MiB
5a21b51+dirty 5.15 MiB 6.67 MiB 1.51 MiB
bbb8d67+dirty 4.98 MiB 6.55 MiB 1.57 MiB

Previous results on branch: feat/replay-runtime-controls

Startup times

Revision Plain With Sentry Diff
1efd073+dirty 3875.87 ms 1233.91 ms -2641.96 ms
e15671a+dirty 3871.92 ms 1241.90 ms -2630.02 ms
b64503c+dirty 3841.92 ms 1219.78 ms -2622.14 ms

App size

Revision Plain With Sentry Diff
1efd073+dirty 5.15 MiB 6.90 MiB 1.75 MiB
e15671a+dirty 5.15 MiB 6.90 MiB 1.75 MiB
b64503c+dirty 5.15 MiB 6.90 MiB 1.75 MiB

@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 554.38 ms 609.86 ms 55.48 ms
Size 50.56 MiB 56.49 MiB 5.93 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
f3215d3+dirty 396.53 ms 436.66 ms 40.13 ms
d0e3b3e+dirty 443.19 ms 480.00 ms 36.81 ms
a5d243c+dirty 499.20 ms 525.62 ms 26.42 ms
7fd0012+dirty 422.41 ms 445.57 ms 23.17 ms
0a147b2+dirty 442.80 ms 522.24 ms 79.44 ms
d7d54c6+dirty 423.54 ms 498.56 ms 75.02 ms
c823bb5+dirty 468.26 ms 516.16 ms 47.90 ms
5789645+dirty 502.66 ms 594.19 ms 91.53 ms
40c9884+dirty 453.76 ms 478.98 ms 25.22 ms
d771cd5+dirty 432.29 ms 464.38 ms 32.09 ms

App size

Revision Plain With Sentry Diff
f3215d3+dirty 48.30 MiB 53.49 MiB 5.19 MiB
d0e3b3e+dirty 49.74 MiB 55.09 MiB 5.34 MiB
a5d243c+dirty 48.30 MiB 53.54 MiB 5.23 MiB
7fd0012+dirty 50.56 MiB 56.46 MiB 5.90 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
d7d54c6+dirty 49.74 MiB 55.09 MiB 5.34 MiB
c823bb5+dirty 48.30 MiB 53.58 MiB 5.28 MiB
5789645+dirty 49.74 MiB 54.85 MiB 5.11 MiB
40c9884+dirty 49.74 MiB 55.09 MiB 5.34 MiB
d771cd5+dirty 49.74 MiB 55.36 MiB 5.62 MiB

Previous results on branch: feat/replay-runtime-controls

Startup times

Revision Plain With Sentry Diff
e15671a+dirty 484.36 ms 517.30 ms 32.94 ms
b64503c+dirty 431.38 ms 442.16 ms 10.78 ms
1efd073+dirty 549.81 ms 601.22 ms 51.41 ms

App size

Revision Plain With Sentry Diff
e15671a+dirty 50.56 MiB 56.49 MiB 5.93 MiB
b64503c+dirty 50.56 MiB 56.49 MiB 5.93 MiB
1efd073+dirty 50.56 MiB 56.49 MiB 5.93 MiB

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/ios/RNSentry.mm
Comment thread packages/core/src/js/replay/getReplay.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit eb4147e. Configure here.

@antonis
antonis added this pull request to stack #6705 September 11, 2026 12:52
@antonis
antonis marked this pull request as ready for review September 14, 2026 08:49
@antonis
antonis requested review from a team, alwx and lucas-zimerman as code owners September 14, 2026 08:49
Comment thread packages/core/src/js/replay/mobilereplay.ts
antonis added a commit to getsentry/sentry-docs that referenced this pull request Sep 14, 2026
Add a "Manually Controlling Session Replay" section to the React Native
Session Replay docs covering the runtime controls exposed via
Sentry.getReplay(): start, startBuffering, stop, pause, resume, flush
(with continueRecording), and getReplayId.

Depends on SDK PR getsentry/sentry-react-native#6703; the availability
version is a placeholder to be confirmed at release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antonis and others added 6 commits September 14, 2026 13:12
Expose JS-accessible Session Replay runtime controls through a shared
`Replay` interface reachable on every platform via `Sentry.getReplay()`:
`start`, `startBuffering`, `stop`, `pause`, `resume` and `flush`.

Mobile forwards each control across the bridge to the native SDKs
(sentry-java `Sentry.replay()` on Android, `SentrySDK.internal.replay`
on iOS). On Web the upstream browser Replay methods are used, with
`pause()`/`resume()` degrading to a logged no-op since the browser
Session Replay SDK does not expose them.

The bridge calls degrade gracefully when native is disabled, the module
is not linked, or an older cached native binary predates a method, so
they never throw and never crash the host app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address PR review findings on the Session Replay runtime controls:

- Invalidate the cached replay id when start/startBuffering/stop/flush
  complete so getReplayId() re-reads the native id instead of returning a
  stale value (fixes wrong DSC/log/metric linking after a control changes
  the native replay identity).
- Wrap the Android replay control methods in try/catch so a native failure
  logs and rejects the promise rather than crashing the host app or hanging
  the JS call.
- Re-add getRecordingMode() to the exported Replay interface as optional so
  the browser integration keeps it typed while mobile omits it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The native flushReplay() always keeps recording after a flush (a buffered
replay is converted to a session and continues), matching the web default of
continueRecording: true. When the caller passes continueRecording: false,
stop the replay once the flush completes so the option is functional on mobile
instead of being silently ignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On React Native Web the mobile replay integration registers as a no-op
stub, so a universal app that installs both integrations would get the
no-op back from getReplay() and its runtime controls would silently do
nothing. Resolve the browser integration first on Web (and the mobile
one on native).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antonis
antonis force-pushed the feat/replay-runtime-controls branch from bd347fb to 73a4bd4 Compare September 14, 2026 11:12
Comment thread packages/core/src/js/replay/mobilereplay.ts Outdated
…call rejects

The `stop()` and `flush({ continueRecording: false })` controls chained
`invalidateCachedReplayId` only on the success path, so a rejected
`NATIVE.stopReplay()` (which can occur on Android) would skip invalidation
and leave a potentially stale id in the JS cache.

Route both through a small `settleAndInvalidateReplayId` helper that
invalidates the cache whether the native call resolves or rejects, then
re-throws to preserve the caller-owned rejection. Invalidation only forces
the next `getReplayId()` to re-read from native, so doing it on failure is
always safe. Add coverage for both rejection paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/core/ios/RNSentry.mm Outdated
Comment thread packages/core/ios/RNSentry.mm Outdated
The new iOS replay control methods (startReplay, startReplayBuffering,
stopReplay, pauseReplay, resumeReplay, flushReplay) called into
RNSentryInternal and resolved without catching exceptions. A catchable
NSException raised from the Cocoa replay path (or Foundation frames it
touches) would propagate to the bridge and crash the host app, contrary
to the iOS bridge convention (packages/core/ios/AGENTS.md) and the
guarded pattern already used by captureReplayWithReturnValue and the
Android controls.

Each method now wraps the native call and resolve in @try/@catch,
logging and rejecting with the shared @"SentryReactNative" code so a
native exception neither crashes the app nor leaves the JS promise
unsettled. Note this does not catch Swift traps (fatalError,
force-unwrap, precondition), which are uncatchable by design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antonis
antonis requested a review from alwx September 14, 2026 12:23

@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.

Public API changes looks good from Flutter POV 👍

I assume the iOS stop() not clearing the native replayId will be fixed in the next Cocoa release right?

@antonis

antonis commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for checking @buenaflor 🙇

I assume the iOS stop() not clearing the native replayId will be fixed in the next Cocoa release right?

Yes, the fix is already merged.

@antonis
antonis merged commit 47b445f into main Sep 14, 2026
137 of 141 checks passed
@antonis
antonis deleted the feat/replay-runtime-controls branch September 14, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose native Replay runtime controls in React Native Programatically start recording.

3 participants