Skip to content

Bug: Glasses Video Session Terminated Immediately — SESSION_ENDED_BY_DEVICE

Date: 2026-05-22
Severity: High (recording fails entirely when glasses not worn)
Status: Fixed

Bug Description

After upgrading to Meta DAT SDK 0.7.0, recording with selectedDevice: "glasses" (audio+video mode) always fails with DatException: Session is already stopped. The glasses make a brief connection sound, then the session terminates immediately and the whole recording session (including successfully-started audio) is cleaned up.

Evidence

Metro log sequence:

capture_audio_started {"activeAudioDevice": "type=7 name=A065 id=973"}  ← SCO audio OK
capture_stream_session_starting
recording_device_rehydrate_on_foreground  ← app backgrounded/foregrounded during stream start
session_cleanup_ended
recording_control_start_failed: DatException: Session is already stopped

Logcat session errors:

SESSION ERROR: SESSION_ENDED_BY_DEVICE — Session ended by device
SESSION ERROR: NO_ELIGIBLE_DEVICE — No eligible device found  (cascade)

Meta View device state at time of failure:

isMounted: false
cameraState: CAPTURE_READY
isSessionVerified: false

Root Cause

In 0.7.0 the Meta DAT SDK enforces that the glasses must be physically worn (mounted/donned) for a camera/video DeviceSession to stay active. When isMounted: false, the glasses firmware sends SESSION_ENDED_BY_DEVICE within milliseconds of the session connecting.

The actual bug was a race condition in session initialization: deviceSession.start() queues the state transition but doesn't wait for it to complete. We then immediately called deviceSession.addStream(), which failed with DatException: Session is already stopped because:

  1. The session state transition STARTED→STOPPED completed faster than the coroutine could execute addStream()
  2. Or, the firmware rejected the session before even the state machine reached STARTED due to isMounted: false

Either way, the session fails before we can add the stream.

Fix

Modified WearablesHelpers.createStreamSession() in WearablesHelpers.kt to wait for the session to reach DeviceSessionState.STARTED before calling addStream(). This ensures the SDK has fully initialized the session before we try to add a stream to it.

The wait has a 5-second timeout. If the glasses firmware rejects the session (due to not being mounted or other reasons), the session will transition to STOPPED and the timeout will catch that as an error, which gets properly logged and propagated.

Code change:

deviceSession.start()

// NEW: Wait for the session to reach STARTED state before adding the stream
val sessionStarted = withTimeout(5_000) {
  deviceSession.state.first { it == DeviceSessionState.STARTED }
}
Log.d("WearablesHelpers", "SESSION: state reached $sessionStarted, now adding stream")

// Then add the stream (now safe because session is confirmed STARTED)
val stream = deviceSession.addStream(
  StreamConfiguration(
    videoQuality = VideoQuality.MEDIUM,
    frameRate = 2,
  )
).getOrThrow()

Verification

  • With glasses worn: Full audio+video recording proceeds normally. Stream initialization completes, and frames are captured.
  • With glasses not worn: Session initialization fails with clear error message in logcat. The TypeScript layer catches this and falls back to audio-only recording (existing fallback at capture-session.ts:337).
  • No regression: glasses-audio mode (audio-only) unaffected.

The fix allows video streaming to work when glasses are worn while preserving the audio-only fallback for cases where the glasses are not available.