You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Video track does not fall back to default device on TrackEnded, unlike audio #2035
In LocalParticipant.handleTrackEnded, when a device disconnects (e.g. an external webcam is unplugged) and the corresponding MediaStreamTrack fires ended, the SDK explicitly falls back to the default device for audio tracks, but not for video tracks:
if(isLocalAudioTrack(track)){// fall back to default device if availableawaittrack.restartTrack({deviceId: 'default'});}else{awaittrack.restartTrack();}
For video, restartTrack() is called with no override, so it retries against the same (now-disconnected) deviceId constraint, fails again, and the surrounding catch mutes the track instead of falling back:
}catch(e){this.log.warn(`could not restart track, muting instead`,getLogContextFromTrack(track));awaittrack.mute();}
The result: unplugging an external microphone falls back seamlessly to the default mic, but unplugging an external webcam just turns the camera off, even though a built-in/default camera is available.
I confirmed this is still present, unchanged, on the latest release (v2.21.0) — same if (isLocalAudioTrack(track)) check, no equivalent branch for isLocalVideoTrack(track).
Reproduction
Join a room with an external webcam selected as the camera source.
Physically disconnect the webcam.
Observe: the local video track ends and gets muted (camera turns off), instead of falling back to the default/built-in camera the way an external microphone falls back to the default mic in the same scenario.
Expected behavior
When a video track ends due to a device disconnect (and the track is not user-muted), it should retry restartTrack({ deviceId: 'default' }), mirroring the existing audio behavior, so the camera falls back to the default device instead of going dark.
Suggested fix
if(isLocalAudioTrack(track)||isLocalVideoTrack(track)){// fall back to default device if availableawaittrack.restartTrack({deviceId: 'default'});}
(Since this branch is only reached when isLocalAudioTrack(track) || isLocalVideoTrack(track) is already true per the outer else if, this can also simply be unconditional.)
Reproduction repo/live example
N/A — traced directly in the SDK source while debugging camera-off behavior in our app; happy to share more details if useful.
SDK version
livekit-client 2.15.14 through 2.21.0 (latest at time of filing)
Can you confirm that you can reproduce this issue on meet.livekit.io ?
The automatic restart should only fail for video tracks if the specified constraints use exact constraints. In this case the assumption is that it might be undesired to switch to a different camera automatically.
For video input devices the notion of default devices unfortunately doesn't exist on the system level in the same way it exists for audio input devices, that's why the two paths differ here.
Activity
lukasIO commentedon Aug 4, 2026
Thanks for the report!
Can you confirm that you can reproduce this issue on meet.livekit.io ?
The automatic restart should only fail for video tracks if the specified constraints use
exactconstraints. In this case the assumption is that it might be undesired to switch to a different camera automatically.For video input devices the notion of
defaultdevices unfortunately doesn't exist on the system level in the same way it exists for audio input devices, that's why the two paths differ here.