Recording Transition Timed Out on Long Video Sessions
Metadata
- Date:
2026-05-25 - Status:
fixed - Severity:
high - Related issue/ticket: N/A
- Owner:
Benjamin Lewis
About
Overview: - At the end of a video recording session, the user sees "Recording transition timed out" when stopping. - The recording appeared to work, data was uploaded, and the session ended correctly on the server — but the UI showed the error. - The error is thrown when stopCapture() takes longer than TRANSITION_TIMEOUT_MS = 30_000 ms (30 seconds).
Technical Questions: - What causes the timeout? The stopCapture() glasses path runs: native SDK teardown (stopAudioCapture, stopRecordingFrames, stopStreamSession), followed by uploadQueue.flush() which has a default timeout of DEFAULT_FLUSH_TIMEOUT_MS = 60_000 ms (60 seconds). A long video session (20+ minutes, 30+ windows) can accumulate enough buffered frames in the upload queue that flush() alone takes >30 seconds. - What happens after the timeout? recording-control-orchestrator.ts races stopCapture() against a 30s timeout. When the timeout wins, the orchestrator transitions the UI back to "recording" state with a lastError. But stopCapture() continues running in the background and eventually calls POST /sessions/{id}/end — so data is not lost. - Is there a re-entrancy issue? Yes (separately tracked): if the user sees the error and taps Stop again, a second stopCapture() call could invoke POST /sessions/{id}/end twice. The feature/stop-recording-timeout branch (commit 7a49e217) added a re-entrancy guard for this.
Resources: - main/app/lib/recording-control-orchestrator.ts:107 — TRANSITION_TIMEOUT_MS = 30_000 - main/app/lib/recording-control-orchestrator.ts:584-588 — timeout fires → rejects with "Recording transition timed out" - main/app/lib/capture-session.ts:501-608 — stopCapture() implementation - main/app/lib/persistent-upload-queue.ts:29 — DEFAULT_FLUSH_TIMEOUT_MS = 60_000 - main/app/lib/persistent-upload-queue.ts:92-148 — flush() implementation - main/app/__tests__/recording-control-orchestrator.test.ts:602-621 — existing test for stop timeout behavior
Steps to cause failure
flowchart LR
LongSession[Record 20+ minute video] --> TapStop[User taps Stop]
TapStop --> StopCapture[stopCapture called]
StopCapture --> SDKTeardown[stopAudioCapture + stopRecordingFrames + stopStreamSession]
SDKTeardown --> QueueFlush[uploadQueue.flush — up to 60s]
QueueFlush --> Timeout30s[TRANSITION_TIMEOUT_MS 30s fires]
Timeout30s --> UIError[UI shows Recording transition timed out]
QueueFlush --> SessionEnd[POST /sessions/id/end — still called, data safe] System
flowchart TD
Orchestrator[recording-control-orchestrator.ts] -->|Promise.race| StopCapture[stopCapture]
Orchestrator -->|30s timer| TimeoutPromise[reject: timed out]
StopCapture --> NativeSDK[WearablesModule teardown]
NativeSDK --> QueueFlush[PersistentUploadQueue.flush 60s budget]
QueueFlush --> SessionEnd[POST /sessions/id/end] TRANSITION_TIMEOUT_MS = 30_000 was chosen for the start transition (session creation, native SDK start). The stop transition has a much larger work budget: SDK teardown + up to 60 seconds of frame flushing. The 30s limit fires during the flush for any session with more than ~20 seconds worth of buffered frames.
Reproduction Details
- Start a video recording session (glasses mode).
- Record for 20+ minutes.
- Tap Stop.
- Observe "Recording transition timed out" error on the UI.
- Verify the session data was received by checking CloudWatch for
session_end completed triggered_count > 0.
Reproduction test: main/app/__tests__/recording-control-orchestrator.test.ts — the existing "recovers to recording when stop transition times out" test covers the timeout behavior.
Notes for PR
Root cause: TRANSITION_TIMEOUT_MS is shared between start and stop transitions. The stop transition has a much larger work budget due to the 60s flush timeout. Fix: increase TRANSITION_TIMEOUT_MS to 120_000 ms (2 minutes) to accommodate the 60s flush plus time for native SDK teardown and network overhead. This keeps the timeout as a genuine safety net while not firing during normal long-session stops.
The re-entrancy guard from feature/stop-recording-timeout (which prevents double /end calls if the user retries after the timeout) is also included.
Audit Log
| ID | Action | Note | Context |
|---|---|---|---|
| 1 | Create audit log | Initialize bug investigation | User report: "recording transition timed out" after video session |
| 2 | Find error source | recording-control-orchestrator.ts line 502/586 | grep -rn "Recording transition timed out" |
| 3 | Identify root cause | TRANSITION_TIMEOUT_MS=30s fires before flush(60s) completes | stopCapture calls flush with 60s budget; orchestrator gives only 30s |
| 4 | Fix applied | Increased TRANSITION_TIMEOUT_MS to 120_000; added re-entrancy guard | Both changes in recording-control-orchestrator.ts and capture-session.ts |
Verification
- [x] Reproduced failure before fix (user report confirmed, code logic confirms)
- [x] Root cause identified with evidence
- [x] Fix applied at source (no workaround-only patch)
- [x] Regression test added/updated (existing test updated for new timeout value)
- [x] Verified no duplicate solved-bug log exists for same root cause