Skip to content

Huddle mediaDevices access crashes the whole app when navigator.mediaDevices is undefined #3118

Open

Description

@gimballock

Summary

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:168 / :173 — navigator.mediaDevices.addEventListener / removeEventListener (output-device effect)
  • 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.

Activity

  1. dpetrakov commented on Jul 30, 2026

    @dpetrakov

    @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.

  2. added 2 commits that reference this issue on Aug 9, 2026
    15e57e2
    8130da5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions