cjpais/Handy · warning
Failed to check macOS permissions:
Error message
Failed to check macOS permissions:
What it means
During startup onboarding checks, App.tsx calls the macOS-specific commands checkAccessibilityPermission and checkMicrophonePermission. If either invoke rejects, this catch logs the failure and deliberately continues into the main app so the user can fix permissions later from settings — a fail-open policy that trades strictness for not bricking first run.
Source
Thrown at src/App.tsx:208
const currentPlatform = platform();
if (hasCompletedOnboarding) {
// Returning user - check if they need to grant permissions first
setIsReturningUser(true);
if (currentPlatform === "macos") {
try {
const [hasAccessibility, hasMicrophone] = await Promise.all([
checkAccessibilityPermission(),
checkMicrophonePermission(),
]);
if (!hasAccessibility || !hasMicrophone) {
await revealMainWindowForPermissions();
setOnboardingStep("accessibility");
return;
}
} catch (e) {
console.warn("Failed to check macOS permissions:", e);
// If we can't check, proceed to main app and let them fix it there
}
}
if (currentPlatform === "windows") {
try {
const microphoneStatus =
await commands.getWindowsMicrophonePermissionStatus();
if (
microphoneStatus.supported &&
microphoneStatus.overall_access === "denied"
) {
await revealMainWindowForPermissions();
setOnboardingStep("accessibility");
return;
}
} catch (e) {
console.warn("Failed to check Windows microphone permissions:", e);View on GitHub (pinned to c89b7bf389)
Solutions
- Verify the two commands are compiled and registered for macOS builds and listed in invoke_handler
- Guard the block with isTauri() so browser dev does not trigger it
- Re-check permissions once the app emits ready / the window gains focus, instead of a single mount-time call
- If failing open is unacceptable for your fork, route to setOnboardingStep('accessibility') on error instead of proceeding
Example fix
// before
} catch (e) {
console.warn("Failed to check macOS permissions:", e);
// If we can't check, proceed to main app and let them fix it there
}
// after
import { isTauri } from "@tauri-apps/api/core";
// ...
} catch (e) {
console.warn("Failed to check macOS permissions:", e);
if (isTauri()) {
await revealMainWindowForPermissions();
setOnboardingStep("accessibility"); // fail toward the permission UI, not silently open
return;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
import { isTauri } from "@tauri-apps/api/core";
if (!isTauri()) { setOnboardingStep("done"); return; } Try / catch
try {
const [a, m] = await Promise.all([checkAccessibilityPermission(), checkMicrophonePermission()]);
/* route on results */
} catch (e) {
console.warn("Failed to check macOS permissions:", e);
// choose fail-open (current) or fail-to-permission-UI deliberately
await revealMainWindowForPermissions();
setOnboardingStep("accessibility");
} Prevention
- Gate permission checks on isTauri() and on the platform() result matching the command's cfg target
- Re-check permissions on window focus rather than only at mount, to survive startup races
- Document the fail-open decision so future changes do not silently remove it
When it happens
Trigger: The invoke rejecting when the backend is not yet ready at component mount; the commands not being registered (e.g. a non-macOS feature build that still passes the platform() check); an underlying macOS API error from the permission query; running the webview in a plain browser during development.
Common situations: Frontend development in Vite without Tauri; early-startup races; development builds where plugin-os reports 'macos' but the permission commands are cfg-gated out.
Related errors
- Failed to initialize after permission grant:
- Failed to check Windows microphone permissions:
- Failed to check Windows microphone permissions:
- Failed to show main window for permission onboarding:
- Failed to fetch secure input status:
AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17).
Data as JSON: /api/errors/552f89f530139476.
Report an issue: GitHub.