Skip to content

fix(replay): Release MediaMuxer when the encoder fails to start#5607

Draft
runningcode wants to merge 2 commits into
mainfrom
no/fix-replay-muxer-leak-on-start-failure
Draft

fix(replay): Release MediaMuxer when the encoder fails to start#5607
runningcode wants to merge 2 commits into
mainfrom
no/fix-replay-muxer-leak-on-start-failure

Conversation

@runningcode

Copy link
Copy Markdown
Contributor

📜 Description

The MediaMuxer is opened eagerly in SimpleVideoEncoder's constructor, but its release() was only reachable on paths that assume start() succeeded. Two cases leaked it when the encoder failed to start:

  • ReplayCache.createVideoOf constructed the encoder and called start() in a single expression (SimpleVideoEncoder(...).also { it.start() }). If start() threw, the assignment to the encoder field never happened, so the only caller of release() could never run.
  • SimpleVideoEncoder.release() released the muxer as the last statement of the try block, after draining and stopping the codec. Draining a codec that never started throws, which jumped to the catch and skipped the muxer release.

The fix releases the encoder if start() throws (then rethrows), and always releases the muxer from a finally block so it is freed even when draining/stopping the codec fails.

💡 Motivation and Context

This surfaced as a CloseGuard "A resource was acquired at attached stack trace but never released" warning pointing at the MediaMuxer allocation in SimpleMp4FrameMuxer. It is a real resource leak on devices where the encoder fails to start, not just a test artifact. Complements #5583, which closed the no-frames and never-started paths.

💚 How did you test it?

Added a regression test (ReplayCacheTest.releases the muxer when the encoder fails to start) that forces start() to fail via the test MediaCodec shadow and asserts, through ShadowCloseGuard.getErrors(), that no MediaMuxer leak is reported. Verified the test fails without the fix (with the exact Explicit termination method 'release' not called error) and passes with it. The full ReplayCacheTest suite is green.

📝 Checklist

  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.

🔮 Next steps

None.

runningcode and others added 2 commits June 23, 2026 14:41
The MediaMuxer is opened eagerly in SimpleVideoEncoder's constructor, but
its release() was only reachable on paths that assume start() succeeded.
Two cases leaked it:

- createVideoOf constructed the encoder and called start() in one
  expression, so when start() threw the encoder was never assigned and
  release() could never run.
- SimpleVideoEncoder.release() released the muxer as the last statement of
  the try block, after draining and stopping the codec. Draining a codec
  that never started throws, skipping the muxer release.

Release the encoder if start() throws, and always release the muxer from a
finally block so it is freed even when draining/stopping the codec fails.
This surfaced as a CloseGuard "resource was acquired but never released"
warning. Complements #5583.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sentry

sentry Bot commented Jun 23, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 327.00 ms 385.27 ms 58.27 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
18c0bc2 306.73 ms 349.77 ms 43.03 ms
0eaac1e 316.82 ms 357.34 ms 40.52 ms
d15471f 303.49 ms 439.08 ms 135.59 ms
fc5ccaf 276.52 ms 370.46 ms 93.93 ms
e2dce0b 308.96 ms 360.10 ms 51.14 ms
5b1a06b 352.27 ms 413.70 ms 61.43 ms
37ec571 366.04 ms 424.28 ms 58.23 ms
9fbb112 361.43 ms 427.57 ms 66.14 ms
bbc35bb 324.88 ms 425.73 ms 100.85 ms
ff8eea4 313.42 ms 337.08 ms 23.66 ms

App size

Revision Plain With Sentry Diff
18c0bc2 1.58 MiB 2.13 MiB 557.33 KiB
0eaac1e 1.58 MiB 2.19 MiB 619.17 KiB
d15471f 1.58 MiB 2.13 MiB 559.54 KiB
fc5ccaf 1.58 MiB 2.13 MiB 557.54 KiB
e2dce0b 0 B 0 B 0 B
5b1a06b 0 B 0 B 0 B
37ec571 0 B 0 B 0 B
9fbb112 1.58 MiB 2.11 MiB 539.18 KiB
bbc35bb 1.58 MiB 2.12 MiB 553.01 KiB
ff8eea4 1.58 MiB 2.28 MiB 718.64 KiB

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.

1 participant