diff --git a/packages/web/src/hooks/useAudioDevices.ts b/packages/web/src/hooks/useAudioDevices.ts index b30210f7..6c39b567 100644 --- a/packages/web/src/hooks/useAudioDevices.ts +++ b/packages/web/src/hooks/useAudioDevices.ts @@ -139,7 +139,11 @@ export function useAudioDevices(): UseAudioDevicesResult { // Enumerate when permission grants; refresh on devicechange. useEffect(() => { - if (permState !== 'granted') return; + if (permState !== 'granted') { + setInputs([]); + setOutputs([]); + return; + } enumerate(); const onChange = () => { enumerate(); }; navigator.mediaDevices.addEventListener('devicechange', onChange); diff --git a/packages/web/src/hooks/useLiveKit.ts b/packages/web/src/hooks/useLiveKit.ts index bc31830b..f9af6001 100644 --- a/packages/web/src/hooks/useLiveKit.ts +++ b/packages/web/src/hooks/useLiveKit.ts @@ -436,6 +436,10 @@ export function useLiveKit() { let copy = 'Microphone could not be restored'; try { + // Probe is intentionally constraint-free — we want to know whether the + // device is reachable, not whether full voice constraints succeed. Adding + // constraints here would create probe-vs-acquire skew (probe might fail + // for a constraint that AudioManager would have negotiated around). const probe = await navigator.mediaDevices.getUserMedia({ audio: deviceId === 'default' ? true : { deviceId: { exact: deviceId } }, });