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
- Open System Settings > Privacy & Security > Microphone, click '+', add the Electron/Orca app, ensure its toggle is on, then restart Orca.
- Reset the denied decision first: run `tccutil reset Microphone <bundle-id>` (or `tccutil reset Microphone` for all), then relaunch so the prompt re-appears.
- Verify Info.plist includes NSMicrophoneUsageDescription and the entitlements/macOS hardened runtime permits the microphone; without it the prompt never shows.
- 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
- Include NSMicrophoneUsageDescription in Info.plist and the mic entitlement in hardened runtime.
- On first launch, call askForMediaAccess('microphone') proactively before the user starts speech.
- Reset a stuck 'denied' state with tccutil reset Microphone during development.
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
- Microphone permission denied
- accessibility_error
- Failed to start microphone recording
- invalid_argument
- [plain-node-entry-guard] "${entryName}" reaches chunk "${chu
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/810517917c47a321.
Report an issue: GitHub.