stablyai/orca · error · Error

Microphone access not granted. In System Settings > Privacy

Error message

Microphone access not granted. In System Settings > Privacy & Security > Microphone, click "+" and add the Electron app, then restart Orca.

What it means

Thrown during speech/microphone setup on macOS when systemPreferences.getMediaAccessStatus('microphone') does not return 'granted' even after an explicit askForMediaAccess('microphone') call. This is the Electron TCC (Transparency, Consent, and Control) layer: the OS must list the Electron binary under System Settings > Privacy & Security > Microphone, otherwise getUserMedia returns a silent zero-filled stream. The error tells the user how to grant the permission manually because the programmatic prompt can be dismissed or denied.

Source

Thrown at src/main/ipc/speech.ts:128

          .catch(() => {})
      }
      const cleanupSessionListener = (): void => {
        window.off('closed', cleanupOnWindowClosed)
      }
      window.once('closed', cleanupOnWindowClosed)

      try {
        // Why: on macOS, the Electron binary needs explicit TCC permission for
        // the microphone. Without it, getUserMedia succeeds but returns a silent
        // stream (all zeros). Check status and attempt to trigger the system
        // permission prompt if not yet granted.
        if (process.platform === 'darwin') {
          const micStatus = systemPreferences.getMediaAccessStatus('microphone')
          if (micStatus !== 'granted') {
            await systemPreferences.askForMediaAccess('microphone')
            const newStatus = systemPreferences.getMediaAccessStatus('microphone')
            if (newStatus !== 'granted') {
              throw new Error(
                'Microphone access not granted. In System Settings > Privacy & Security > Microphone, ' +
                  'click "+" and add the Electron app, then restart Orca.'
              )
            }
          }
        }

        if (hotwords && hotwords.length > 0) {
          const content = `${hotwords.map((w) => `${w} :2.0`).join('\n')}\n`
          const hotwordsFilePath = getHotwordsFilePath(content)
          await writeFile(hotwordsFilePath, content, 'utf-8')
          resolvedHotwordsPath = hotwordsFilePath
        }

        if (windowClosed || window.isDestroyed()) {
          cleanupSessionListener()
          if (resolvedHotwordsPath) {
            unlink(resolvedHotwordsPath).catch(() => {})

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Open System Settings > Privacy & Security > Microphone, click '+', add the Electron/Orca app, ensure its toggle is on, then restart Orca.
  2. Reset the denied decision first: run `tccutil reset Microphone <bundle-id>` (or `tccutil reset Microphone` for all), then relaunch so the prompt re-appears.
  3. Verify Info.plist includes NSMicrophoneUsageDescription and the entitlements/macOS hardened runtime permits the microphone; without it the prompt never shows.
  4. If an MDM profile is enforcing the restriction, work with the device administrator to allowlist the app.
Defensive patterns

Strategy: try-catch

Validate before calling

import { systemPreferences } from 'electron'
function isMicGranted(): boolean {
  return process.platform !== 'darwin' || systemPreferences.getMediaAccessStatus('microphone') === 'granted'
}

Try / catch

try {
  await startSpeechRecognition({ hotwords })
} catch (e) {
  if (/Microphone access not granted/.test((e as Error).message)) {
    showMacOSPermissionInstructions()
  } else throw e
}

Prevention

When it happens

Trigger: process.platform === 'darwin' and the mic status is 'denied', 'restricted', or 'not-determined' both before and after systemPreferences.askForMediaAccess('microphone'). Happens on first run, after the user clicked 'Don't Allow' on a prior prompt, or after a macOS/MDM policy revoked the entitlement.

Common situations: First launch on a new Mac where TCC has no entry for the app. The user previously denied access and the choice is sticky. An MDM profile forces microphone privacy restrictions. The app bundle was rebuilt/re-signed so macOS treats it as a new identity and resets TCC state.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/810517917c47a321. Report an issue: GitHub.