Skip to content

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:107TRANSITION_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-608stopCapture() implementation - main/app/lib/persistent-upload-queue.ts:29DEFAULT_FLUSH_TIMEOUT_MS = 60_000 - main/app/lib/persistent-upload-queue.ts:92-148flush() 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

  1. Start a video recording session (glasses mode).
  2. Record for 20+ minutes.
  3. Tap Stop.
  4. Observe "Recording transition timed out" error on the UI.
  5. 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