getsentry / getsentry/sentry-java
Android Replay: BufferCaptureStrategy.stop() can persist replay state after deleting replay cache dir
- Linguagem predominante
- Kotlin
- Estrelas
- 1.4k
- Forks
- 478
- Merge médio
- 3d 4h
- PRs com merge (30d)
- 72
Descrição
### Problem Statement
Android Session Replay can throw an internal error during replay teardown:
```text
Failed to execute task CaptureStrategy.runInBackground
java.io.IOException: No such file or directory
...
at io.sentry.android.replay.ReplayCache.persistSegmentValues
...
at io.sentry.android.replay.capture.BufferCaptureStrategy.stop
```
This appears to happen when stopping a buffering replay.
### Likely root cause
`BufferCaptureStrategy.stop()` currently deletes the replay cache directory and then updates replay state in the same task:
- delete replay cache dir
- set `currentSegment = -1`
The `currentSegment` setter persists state through `ReplayCache.persistSegmentValues(...)`.
On the replay worker thread, that persistence runs inline, so it can try to recreate/write `.ongoing_segment` after the replay directory has already been deleted.
That leads to:
```text
java.io.IOException: No such file or directory
at java.io.File.createNewFile
at io.sentry.android.replay.ReplayCache.persistSegmentValues
```
### Why this matters
This is at least noisy internal error logging, but it may also contribute to brittle replay lifecycle behavior in manual/custom flows that use combinations of:
- `flush()`
- `pause()`
- `stop()`
- `startBuffering()`
In our investigation, this showed up while testing a custom User Feedback + Replay workaround, where replay behavior was already timing-sensitive.
### Relevant code path
Conceptually, the flow is:
- `BufferCaptureStrategy.stop()`
- `FileUtils.deleteRecursively(replayCacheDir)`
- `currentSegment = -1`
- `persistSegmentValues(...)`
- `ongoingSegmentFile?.createNewFile()`
- parent folder no longer exists
### Expected behavior
Stopping replay should not try to persist replay state into a directory that has already been deleted.
### Actual behavior
Replay teardown logs `No such file or directory` from `ReplayCache.persistSegmentValues`.
### Suggested fix direction
Any of these would likely resolve it:
- clear/persist mutable state before deleting the replay directory
- mark the cache closed before state mutation can persist
- guard `persistSegmentValues` against a missing/deleted parent directory during teardown
- avoid persisting teardown sentinel values like `currentSegment = -1` once shutdown has begun
### Customer-visible context
This was observed during a real customer reproduction involving Android Session Replay plus custom in-app User Feedback, where the workaround flow exercised replay stop/restart behavior repeatedly.
### Logs
```text
Failed to execute task CaptureStrategy.runInBackground
java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively0(Native Method)
at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:373)
at java.io.File.createNewFile(File.java:1022)
at io.sentry.android.replay.ReplayCache.persistSegmentValues$sentry_android_replay_release(ReplayCache.kt:290)
at io.sentry.android.replay.capture.BaseCaptureStrategy$special$$inlined$persistableAtomic$default$2$2.invoke(BaseCaptureStrategy.kt:240)
at io.sentry.android.replay.capture.BaseCaptureStrategy$special$$inlined$persistableAtomic$default$2.runInBackground(BaseCaptureStrategy.kt:227)
at io.sentry.android.replay.capture.BaseCaptureStrategy$special$$inlined$persistableAtomic$default$2.setValue(BaseCaptureStrategy.kt:239)
at io.sentry.android.replay.capture.BaseCaptureStrategy.setCurrentSegment(BaseCaptureStrategy.kt:88)
at io.sentry.android.replay.capture.BufferCaptureStrategy.stop$lambda$0(BufferCaptureStrategy.kt:84)
```
Guia de contribuição
Direção de pesquisa
Comece com BufferCaptureStrategy.kt e ReplayCache.kt, acompanhando BufferCaptureStrategy.stop() por meio do setter de currentSegment e de persistSegmentValues(). Reproduza o caminho de teardown do replay em buffer, especialmente após excluir o diretório de cache do replay, e verifique se interromper o replay não registra mais o erro No such file or directory.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- android, kotlin
- Domínio
- mobile-dev
- Tipo de issue
- Bug
- Dificuldade
- 3/5
- Tempo estimado
- 1-2 dias
- Status de atividade
- Ativa
- Clareza
- Razoavelmente clara
- Facilidade para iniciantes
- 70/100