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
Huddle mediaDevices access crashes the whole app when navigator.mediaDevices is undefined #3118
In a WKWebView running in a non-secure context, navigator.mediaDevices is undefined. The huddle code accesses it unguarded in mount-time effects, so the entire React tree crashes to the "Something went wrong" error boundary with:
undefined is not an object (evaluating 'navigator.mediaDevices.enumerateDevices')
Where (v0.4.26, a31fc4d2)
Unguarded, and reached from useEffect on mount (i.e. crashes without the user ever starting a huddle):
desktop/src/features/huddle/lib/useAudioDevices.ts:20 — navigator.mediaDevices.enumerateDevices(), plus addEventListener / removeEventListener at :30 / :32
desktop/src/features/huddle/HuddleContext.tsx:374 — navigator.mediaDevices.getUserMedia (huddle-join path; crashes on join rather than on mount)
Prior art in the codebase
The profile avatar path already guards this exact pattern: desktop/src/features/profile/lib/animatedAvatarCapture.ts:128 — if (!navigator.mediaDevices?.enumerateDevices) return [];
So the huddle paths are just inconsistent with the app's own established convention. (Note getUserMedia at animatedAvatarCapture.ts:154 is likewise unguarded, if you want to standardize across both features.)
Repro
Run the desktop app in a WKWebView context where navigator.mediaDevices is unavailable (e.g. a non-secure-context webview). On mount of the huddle context, the app crashes to the error boundary.
Suggested fix
Guard the huddle mediaDevices accesses the same way the avatar code does — degrade to an empty device list / skip listener registration rather than throw. The mount-time effects should no-op when navigator.mediaDevices?.enumerateDevices is absent; the getUserMedia join path should surface a clear "microphone unavailable" error instead of the opaque crash. Happy to open a PR.
@gimballock you offered to open a PR on this – I hope it is alright that I went ahead, since the issue had been quiet for a few days. Happy to close mine if you already have one in flight.
The fix follows your suggestion directly: all five call sites now degrade instead of throwing, using the same guard shape as profile/lib/animatedAvatarCapture.ts:128. Two details worth flagging, both from working through your list:
The output-device effect in HuddleContext.tsx needed the guard placed after the invoke calls, not before. The list itself comes from Rust via list_audio_output_devices and works fine without mediaDevices – only the devicechange listener needs guarding, so an early return at the top of the effect would have silently dropped output devices on every platform.
The getUserMedia join path throws a sentinel that formatHuddleActionError maps to "Microphone access isn't available in this window. Try restarting Buzz." rather than a silent no-op, which matches what you asked for.
I left animatedAvatarCapture.ts:154 alone – you flagged it in the issue, but it is a different feature, and the guide is explicit about not bundling drive-by changes. Easy follow-up if a maintainer wants it.
Activity
dpetrakov commentedon Jul 30, 2026
@gimballock you offered to open a PR on this – I hope it is alright that I went ahead, since the issue had been quiet for a few days. Happy to close mine if you already have one in flight.
The fix follows your suggestion directly: all five call sites now degrade instead of throwing, using the same guard shape as
profile/lib/animatedAvatarCapture.ts:128. Two details worth flagging, both from working through your list:HuddleContext.tsxneeded the guard placed after theinvokecalls, not before. The list itself comes from Rust vialist_audio_output_devicesand works fine withoutmediaDevices– only thedevicechangelistener needs guarding, so an early return at the top of the effect would have silently dropped output devices on every platform.getUserMediajoin path throws a sentinel thatformatHuddleActionErrormaps to "Microphone access isn't available in this window. Try restarting Buzz." rather than a silent no-op, which matches what you asked for.I left
animatedAvatarCapture.ts:154alone – you flagged it in the issue, but it is a different feature, and the guide is explicit about not bundling drive-by changes. Easy follow-up if a maintainer wants it.fix(huddle): guard mediaDevices access when the API is unavailable
test(huddle): cover the mount-time crash with a jsdom render