Skip to content

fix(core): Clear the persisted replay id when resetting the scope cache - #6033

Draft
runningcode wants to merge 2 commits into
mainfrom
no/clear-persisted-replay-id-on-init
Draft

fix(core): Clear the persisted replay id when resetting the scope cache#6033
runningcode wants to merge 2 commits into
mainfrom
no/clear-persisted-replay-id-on-init

Conversation

@runningcode

@runningcode runningcode commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

📜 Description

PersistingScopeObserver.resetCache() runs on SDK init and clears the persisted scope so values from the previous process don't leak into the new one. It deletes user, level, request, fingerprint, contexts, extras, tags, trace and transaction, and clears the breadcrumb queue — but it has never touched replay.json.

This adds delete(REPLAY_FILENAME) alongside the others, plus a test in PersistingScopeObserverBatchingTest.

💡 Motivation and Context

A replay id points at a replay recorded by the process that wrote it. Leaving it on disk across an init means ApplicationExitInfoEventProcessor can read a stale id and attach it to an ANR event from the current process. The impact is bounded — the processor falls back to scanning for the newest replay_* folder when the id's folder is gone, and the replay integration overwrites the file once it starts recording — but the id should not survive the reset in the first place.

The intent was there from the start: the comment at the resetCache() call site in Sentry.notifyOptionsObservers already gives replayId as its example of a value that must not reach new events. The implementation just never included it.

Split out of #6031, which is a test-only flakiness fix. Joining a previously discarded Future there revived an assertion that had been silently swallowed since #4181 and turned out never to have been satisfiable, which is how this gap surfaced.

Why it's safe to delete

replay.json is used during init to finalize the replay from the previous process. That handoff is already finished by the time the reset runs, because all three steps are queued on the same single-threaded executor, in this order:

  1. AnrV2Integration.register() — ANR enrichment reads the replay id and writes back the one it resolved
  2. ReplayIntegration.register()finalizePreviousReplay() reads it and finalizes that replay
  3. notifyOptionsObservers()resetCache() deletes it

Steps 1 and 2 are deliberately ordered: AndroidOptionsInitializer has a comment that Anr must be installed before Replay "as ReplayIntegration relies on it to set the replayId in case of an ANR". Step 3 has always come last, which is why AnrV2 events still get the previous process's replay id today.

Nothing reads the file after step 3, so deleting there only affects the next init — which is the point.

Deleting also doesn't introduce a new state to handle. replay.json can already hold EMPTY_ID today, written by the replay integration when a replay stops, and both readers treat a missing file exactly like EMPTY_ID: finalizePreviousReplay() calls cleanupReplays() either way, and ApplicationExitInfoEventProcessor looks for a replay_<id> folder that doesn't exist either way and takes the same fallback.

💚 How did you test it?

New test resetCache clears the replay id left behind by the previous process in PersistingScopeObserverBatchingTest. Verified it fails without the one-line change (expected: null / but was: <afcb46b1140ade5187c4bbb5daa804df>) and passes with it.

Ran :sentry:test, :sentry-android-core:testReleaseUnitTest and :sentry-android-replay:testReleaseUnitTest — all pass.

📝 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.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

None.

runningcode and others added 2 commits September 1, 2026 08:58
resetCache() clears every other persisted scope value on init but leaves
replay.json in place, so a replay id written by a previous process can
still be attached to events from the current one. The reset already runs
after the integrations that consume those values, so deleting it here is
safe.

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

sentry Bot commented Sep 1, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

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