{"record":{"id":"939ee6dbe1a3a6d2","repo":"cjpais/Handy","slug":"failed-to-check-windows-microphone-permissions","errorCode":null,"errorMessage":"Failed to check Windows microphone permissions:","messagePattern":"Failed to check Windows microphone permissions:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/onboarding/AccessibilityOnboarding.tsx","lineNumber":149,"sourceCode":"          });\n        }\n\n        return;\n      }\n\n      try {\n        const microphoneGranted = await hasWindowsMicrophoneAccess();\n\n        setPermissions({\n          accessibility: \"granted\",\n          microphone: microphoneGranted ? \"granted\" : \"needed\",\n        });\n\n        if (microphoneGranted) {\n          await completeOnboarding();\n        }\n      } catch (error) {\n        console.warn(\"Failed to check Windows microphone permissions:\", error);\n        setPermissions({\n          accessibility: \"granted\",\n          microphone: \"granted\",\n        });\n        await completeOnboarding();\n      }\n    };\n\n    checkInitial();\n  }, [completeOnboarding, hasWindowsMicrophoneAccess, onComplete, t]);\n\n  // Polling for permissions after user clicks a button\n  const startPolling = useCallback(() => {\n    if (pollingRef.current || permissionPlatform === null) return;\n\n    pollingRef.current = setInterval(async () => {\n      try {\n        if (permissionPlatform === \"windows\") {","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/cjpais/Handy/blob/c89b7bf38974ad317177389f5bfe28236f0d64e3/src/components/onboarding/AccessibilityOnboarding.tsx#L131-L167","documentation":"In the Windows branch of AccessibilityOnboarding's checkInitial, the app invokes the Tauri command behind hasWindowsMicrophoneAccess() to read the OS microphone privacy setting. If the invoke rejects, this catch logs the failure and deliberately fails open: it marks microphone as granted and auto-completes onboarding. So the message never blocks the user, but it means the real permission state was never established.","triggerScenarios":"The invoke rejecting because the backend is not yet ready when the component mounts; the command not being registered in a stripped/dev build; the Windows privacy registry/service read erroring; or running the frontend via plain `bun run dev` in a browser where Tauri invoke always rejects.","commonSituations":"Iterating UI in Vite outside the Tauri shell; racing between webview load and command handler registration; Windows builds where the privacy-settings query API returns an unexpected error.","solutions":["If it only appears in browser dev, guard with isTauri() from @tauri-apps/api/core before invoking","Confirm the command appears in the tauri generate_handler! invoke_handler list for Windows builds","Retry the check once after a short delay to rule out startup races before failing open","Reconsider the fail-open policy: route the error to the permission step UI instead of auto-granting, so users are not silently onboarded with a broken mic"],"exampleFix":"// before\n} catch (error) {\n  console.warn(\"Failed to check Windows microphone permissions:\", error);\n  setPermissions({ accessibility: \"granted\", microphone: \"granted\" });\n  await completeOnboarding();\n}\n\n// after\nimport { isTauri } from \"@tauri-apps/api/core\";\n// ...\n} catch (error) {\n  if (!isTauri()) return; // browser dev: skip, do not complete onboarding\n  console.warn(\"Failed to check Windows microphone permissions:\", error);\n  setPermissions((p) => ({ ...p, microphone: \"needed\" })); // fail visible, not open\n}","handlingStrategy":"try-catch","validationCode":"import { isTauri } from \"@tauri-apps/api/core\";\nif (!isTauri()) return; // browser dev: skip permission invoke entirely","typeGuard":null,"tryCatchPattern":"try {\n  const granted = await hasWindowsMicrophoneAccess();\n  /* handle */\n} catch (error) {\n  console.warn(\"Failed to check Windows microphone permissions:\", error);\n  // decide policy explicitly: fail-open (current) vs fail-visible\n  setPermissions((p) => ({ ...p, microphone: \"needed\" }));\n}","preventionTips":["Gate all Tauri invokes with isTauri() when the frontend must also run in a browser","Confirm commands are in generate_handler! before wiring a UI flow to them","Do not auto-complete onboarding inside a catch — surface the uncertain state instead"],"tags":["tauri","windows","permissions","onboarding","invoke"],"backgroundTag":"tauri-command-invoke-failed","analyzedSha":"c89b7bf38974ad317177389f5bfe28236f0d64e3","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}