Skip to content

Deadlock between ReplayIntegration.close() and connectivity callbacks blocks main thread (ANR) #5518

@buenaflor

Description

@buenaflor

Integration

sentry-android

Build System

Gradle

AGP Version

8.6.0

Proguard

Disabled

Other Error Monitoring Solution

No

Version

8.41.3

Steps to Reproduce

  1. SentryAndroid.init with Session Replay enabled (e.g. sessionSampleRate = 1.0) — ReplayIntegration registers as a connection status observer and AndroidConnectionStatusProvider
    registers a ConnectivityManager network callback.
  2. Call Sentry.close() from the main thread shortly after init (or whenever a connectivity notification is in flight — Android delivers the current network state immediately after
    callback registration, so closing soon after init hits this reliably).
  3. Race: loop init→close with replay enabled on a loaded emulator; reproduces within ~30 cycles.

Expected Result

Sentry.close() completes and the SDK shuts down cleanly.

Actual Result

Sentry.close() never returns — AB-BA deadlock between ReplayIntegration.lifecycleLock and AndroidConnectionStatusProvider.lock. When close() runs on the main thread this is a permanent
main-thread block (ANR).

  • Closing thread: ReplayIntegration.close() holds lifecycleLock (ReplayIntegration.kt:365) → removeConnectionStatusObserver() waits on the provider lock
    (AndroidConnectionStatusProvider.java:441).
  • ConnectivityThread: updateCacheAndNotifyObservers holds the provider lock while notifying observers (AndroidConnectionStatusProvider.java:274) →
    ReplayIntegration.onConnectionStatusChangedresumeInternal() waits on lifecycleLock (ReplayIntegration.kt:220).
  "main" Waiting
    at io.sentry.android.core.internal.util.AndroidConnectionStatusProvider.removeConnectionStatusObserver(AndroidConnectionStatusProvider.java:441)
    at io.sentry.android.replay.ReplayIntegration.close(ReplayIntegration.kt:370)
    at io.sentry.Scopes.close(Scopes.java:439)
    at io.sentry.Sentry.close(Sentry.java:727)

  "ConnectivityThread" Waiting
    at io.sentry.android.replay.ReplayIntegration.resumeInternal(ReplayIntegration.kt:220)
    at io.sentry.android.replay.ReplayIntegration.onConnectionStatusChanged(ReplayIntegration.kt:393)
    at io.sentry.android.core.internal.util.AndroidConnectionStatusProvider$1.updateCacheAndNotifyObservers(AndroidConnectionStatusProvider.java:277)
    at io.sentry.android.core.internal.util.AndroidConnectionStatusProvider$1.onCapabilitiesChanged(AndroidConnectionStatusProvider.java:255)

Full thread dump attached: sentry_replay_deadlock_trace.txt

Impact

  • CI (ongoing): Recurring 30-minute job timeouts in getsentry/sentry-dart integration tests e.g.
    https://github.com/getsentry/sentry-dart/actions/runs/27229728938/job/80406349339
  • Production (very rare): Any app with replay enabled that calls Sentry.close() or re-inits the SDK at runtime can
    deadlock if a connectivity change lands in the window. When close() runs on the main thread the result is a permanent ANR caused by the SDK, and the ANR trace is effectively undiagnosable for users — just two parked threads inside io.sentry.*.
  • Not affected: apps that never close/re-init Sentry; backgrounding alone is safe (stop() never takes the provider lock).

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions