{"record":{"id":"a3163cc2953f3825","repo":"different-ai/openwork","slug":"macos-denied-microphone-access-enable-openwork-in","errorCode":null,"errorMessage":"macOS denied microphone access. Enable OpenWork in System Settings > Privacy & Security > Microphone, then restart OpenWork.","messagePattern":"macOS denied microphone access\\. Enable OpenWork in System Settings > Privacy & Security > Microphone, then restart OpenWork\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/react-app/domains/session/voice/voice-panel.tsx","lineNumber":496,"sourceCode":"    }\n  }, [addEntry, props.sessionId, requestRealtimeResponse, setRuntimeStatus]);\n\n  const connectRealtime = useCallback(async (audioInput = true) => {\n    const client = props.client;\n    if (!client) throw new Error(\"OpenWork host connection is not ready.\");\n    if (audioInput && !navigator.mediaDevices?.getUserMedia) throw new Error(\"Microphone capture is unavailable in this runtime.\");\n\n    disconnectRealtime(true);\n    setRuntimeStatus(\"connecting\", \"Minting Realtime session...\");\n    const sessionContext = await loadVoiceSessionContext(props.opencodeBaseUrl, props.openworkToken, props.sessionId);\n    const realtimeSession = await client.createVoiceRealtimeSession({ sessionContext });\n\n    const peer = new RTCPeerConnection();\n    voiceRealtime.peer = peer;\n    if (audioInput) {\n      setRuntimeStatus(\"connecting\", \"Requesting microphone...\");\n      const macPermissionGranted = await requestMacMicrophoneAccess();\n      if (!macPermissionGranted) throw new Error(\"macOS denied microphone access. Enable OpenWork in System Settings > Privacy & Security > Microphone, then restart OpenWork.\");\n      const stream = await navigator.mediaDevices.getUserMedia({\n        audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },\n      });\n      voiceRealtime.stream = stream;\n      setMicDiagnostics(stream);\n      for (const track of stream.getAudioTracks()) {\n        track.addEventListener(\"mute\", () => setMicDiagnostics(stream));\n        track.addEventListener(\"unmute\", () => setMicDiagnostics(stream));\n        track.addEventListener(\"ended\", () => setMicDiagnostics(stream));\n        peer.addTrack(track, stream);\n      }\n    } else {\n      setVoiceRuntimeSnapshot((current) => ({ ...current, micDiagnostics: \"Voice command is using typed or injected audio, not the microphone.\" }));\n      peer.addTransceiver(\"audio\", { direction: \"recvonly\" });\n    }\n\n    const audio = document.createElement(\"audio\");\n    audio.autoplay = true;","sourceCodeStart":478,"sourceCodeEnd":514,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/react-app/domains/session/voice/voice-panel.tsx#L478-L514","documentation":"On macOS the panel first calls requestMacMicrophoneAccess() to trigger the OS microphone permission prompt. If macOS denies the permission (user clicked Den, or the app is blocked at the OS level), getUserMedia would fail anyway, so the panel throws with instructions pointing at System Settings.","triggerScenarios":"requestMacMicrophoneAccess() resolves false — the OS-level microphone permission for OpenWork is denied or undetermined-and-dismissed — while connecting with audio input on macOS.","commonSituations":"User previously denied the macOS mic prompt; the packaged app lacks the microphone entitlement / Info.plist NSSMicrophoneUsageDescription so the OS silently denies; enterprise MDM blocking mic access; permission reset via System Settings after an update.","solutions":["Grant OpenWork microphone access in System Settings > Privacy & Security > Microphone, then restart the app","Re-trigger the permission prompt (requestMacMicrophoneAccess) and accept it","Verify the packaged app has NSSMicrophoneUsageDescription and the mic entitlement — rebuild/re-sign if missing","Fall back to audioInput = false (typed voice commands) until permission is granted"],"exampleFix":"// before\nawait connectRealtime(true);\n// after\ntry {\n  await connectRealtime(true);\n} catch (e) {\n  if (String(e).includes(\"macOS denied microphone access\")) {\n    toast.error(\"Enable OpenWork in System Settings > Privacy & Security > Microphone, then retry.\");\n  }\n}","handlingStrategy":"try-catch","validationCode":"const granted = await requestMacMicrophoneAccess();\nif (!granted) {\n  showMacMicSetupDialog();\n  return;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await connectRealtime(true);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"System Settings > Privacy & Security > Microphone\")) {\n    toast.error(\"Grant OpenWork microphone access in System Settings, then restart the app.\");\n  } else throw e;\n}","preventionTips":["Trigger the macOS mic permission prompt early (on first launch) rather than mid-connect","Ship the app with NSSMicrophoneUsageDescription and the mic entitlement","Provide an in-app link that opens System Settings > Microphone","Fall back to typed voice commands when permission is denied"],"tags":["macos","microphone","permissions","voice"],"backgroundTag":"microphone-permission-denied","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}