Skip to content

Disk-persisted data can race during SDK re-init #5959

Description

@sentry-junior

During SDK re-init (Sentry.init while already enabled), previous and new SDK lifecycles can concurrently read/write the same on-disk paths under cacheDirPath. On restart, Scopes.close(true) shuts the old executor down asynchronously rather than blocking, so old flush/session/envelope work can still touch files while the new init writes the same locations.

Impact: corrupted or lost scope/options/session state used for ANR/exit enrichment and crash recovery; possible lost breadcrumbs.

Why this races

Disk-backed surfaces that share paths across re-init

Scope cache (.scope-cache/, via PersistingScopeObserver + CacheUtils):

  • breadcrumbs.json — single QueueFile; old executor may still add/sync while new init resetCache() clears/syncs the same file
  • user.json, tags.json, extras.json, contexts.json, request.json, level.json, fingerprint.json, transaction.json, trace.json, replay.json — plain overwrite/delete of fixed names; no atomic replace
  • New init explicitly resetCache() after options observers run, which collides with any late old-lifecycle flushes

Options cache (.options-cache/, via PersistingOptionsObserver and Android PersistingOptionsCacheGenerationObserver):

  • release.json, proguard-uuid.json, sdk-version.json, environment.json, dist.json, tags.json, replay-error-sample-rate.json, app-last-update-time.json
  • Written on every init via options observers; same fixed paths, non-atomic FileOutputStream overwrite

Session / envelope cache (EnvelopeCache, Android AndroidEnvelopeCache):

  • session.json / previous_session.json — move + rewrite on session start; previous-session finalize also reads/deletes these
  • *.envelope offline cache — old client flush vs new client store/send against same dir
  • crash markers: last_crash, .sentry-native/last_crash, startup_crash (outbox)
  • Android ANR last-reported markers under cache dir

Other fixed-path / shared-dir writers (same cacheDirPath, lifecycle-overlapping on re-init):

  • App-start profiling config file rewritten on init (Sentry.handleAppStartProfilingConfig)
  • Profiling traces dir cleanup on init vs in-flight profile finalization
  • Session Replay dirs (replay_<id>/) under cache dir (ReplayCache); cleanup walks cache dir on integration start
  • ANR profile QueueFiles under cache dir (AnrProfileManager / rotation helper already documents concurrent QueueFile risk)

Highest-confidence race today

Scope persistence breadcrumbs: single shared breadcrumbs.json QueueFile, old executor still draining adds while new init clears it in resetCache(), with only executor ordering (not cross-lifecycle isolation) as the safety net.

Related

Unknowns

  • Whether this has been observed in production beyond code inspection / reasoning about re-init
  • How often customer/integration re-init hits the overlapping-executor window in practice

Raised by Markus Hintersteiner during SDK re-init / disk-persistence discussion.

Requested by markus.hintersteiner.

--

View Junior Session [Sentry]

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions