cjpais/Handy · warning
Failed to fetch secure input status:
Error message
Failed to fetch secure input status:
What it means
SecureInputWarning mounts and immediately fetches macOS Secure Input state via the getSecureInputStatus command, then subscribes to the secure-input-changed event. If the initial fetch rejects, this catch logs it; status stays null so the warning banner stays hidden until a subsequent event arrives. Because the component also listens for changes, the practical impact is limited to missing a pre-existing secure-input state at mount.
Source
Thrown at src/components/SecureInputWarning.tsx:30
/**
* Compact warning banner shown while macOS Secure Input is stuck on.
*
* Secure Input (password fields, Terminal's "Secure Keyboard Entry", a stuck
* loginwindow) blocks key events from reaching Handy's keyboard listener, so
* keyed shortcuts silently stop firing (issue #1578). The backend monitor
* emits `secure-input-changed` on state transitions; `sustained` filters out
* the normal momentary activation from focusing a password field.
*/
const SecureInputWarning: React.FC = () => {
const { t } = useTranslation();
const [status, setStatus] = useState<SecureInputStatus | null>(null);
const [dismissed, setDismissed] = useState(false);
const refresh = useCallback(async () => {
try {
setStatus(await commands.getSecureInputStatus());
} catch (e) {
console.warn("Failed to fetch secure input status:", e);
}
}, []);
useEffect(() => {
refresh();
const unlisten = listen<SecureInputStatus>(
"secure-input-changed",
(event) => setStatus(event.payload),
);
return () => {
unlisten.then((fn) => fn());
};
}, [refresh]);
// Only warn when the user is actually impacted: a binding is degraded
// (side-specific matching widened) or dead (e.g. fn+key), or they ran into
// the blocked shortcut recorder. When the fallback covers everything
// transparently — and nothing else surfaced — stay silent; the backendView on GitHub (pinned to c89b7bf389)
Solutions
- Re-run refresh() after the unlisten promise resolves (subscription confirmed) or on window focus to cover mount races
- Skip mounting the component on non-macOS platforms (it is a macOS-only concern)
- Guard with isTauri() for browser development
- Have the Rust command log its own failure cause so the warn payload identifies the real error
Example fix
// before
useEffect(() => {
refresh();
const unlisten = listen<SecureInputStatus>("secure-input-changed", (event) => setStatus(event.payload));
return () => { unlisten.then((fn) => fn()); };
}, []);
// after
useEffect(() => {
refresh();
const unlisten = listen<SecureInputStatus>("secure-input-changed", (event) => setStatus(event.payload));
unlisten.then(() => refresh()); // re-poll once the subscription is live
return () => { unlisten.then((fn) => fn()); };
}, [refresh]); Defensive patterns
Strategy: retry
Validate before calling
// Only mount on macOS, inside Tauri
{platform() === "macos" && isTauri() && <SecureInputWarning />} Try / catch
const refresh = useCallback(async () => {
try { setStatus(await commands.getSecureInputStatus()); }
catch (e) { console.warn("Failed to fetch secure input status:", e); }
}, []); Prevention
- Re-poll after the event subscription is confirmed live to cover mount races
- Log the backend error detail so non-macOS or watcher-spawn failures are identifiable
- Treat a null status as 'unknown', not 'inactive', in render logic
When it happens
Trigger: The invoke firing before the backend command/watcher is ready; the command erroring on non-macOS platforms if the component is mounted there; the secure-input watcher thread failing to spawn in the Rust backend; browser dev where invoke rejects.
Common situations: Mount-time races at app startup; development outside Tauri; macOS keychain/SecureInput API edge cases where the poll errors.
Related errors
- failed to create secure input warning item
- Failed to check Windows microphone permissions:
- Failed to sync language from settings:
- Failed to sync theme from settings:
- Failed to show main window for permission onboarding:
AI-assisted analysis of cjpais/Handy@c89b7bf389 (2026-08-17).
Data as JSON: /api/errors/de2eed336789b2eb.
Report an issue: GitHub.