cjpais/Handy · error
failed to create secure input warning item
Error message
failed to create secure input warning item
What it means
Panic when muda's MenuItem::with_id fails to create the optional 'secure_input_warning' tray item on macOS. The item is only built while Secure Input is engaged (secure_input warning active). Creation fails when the menu resource cannot be allocated — typically a duplicated id within the menu, an invalid id/label, or tray/menu rebuild racing app teardown when the app handle is no longer usable.
Solutions
- Ensure the id 'secure_input_warning' is used exactly once per menu build
- Guard tray rebuild paths against app teardown (check the app is still running before rebuilding)
- Treat this optional warning item as skippable: log and continue without it instead of expecting
Example fix
// before
let secure_input_warning = crate::secure_input::tray_warning_active(app).then(|| {
MenuItem::with_id(app, "secure_input_warning", &label, true, None::<&str>)
.expect("failed to create secure input warning item")
});
// after — optional item degrades instead of aborting tray creation
let secure_input_warning = crate::secure_input::tray_warning_active(app).and_then(|_| {
match MenuItem::with_id(app, "secure_input_warning", &label, true, None::<&str>) {
Ok(item) => Some(item),
Err(e) => {
log::warn!("skipping secure input tray item: {e}");
None
}
}
}); Defensive patterns
Strategy: fallback
Try / catch
match MenuItem::with_id(app, "secure_input_warning", &label, true, None::<&str>) {
Ok(item) => Some(item),
Err(e) => {
log::warn!("secure input tray item skipped: {e}");
None // build the tray without the optional warning line
}
} Prevention
- Treat optional tray items as skippable: log-and-continue instead of expect
- Guard tray rebuilds against app teardown (theme changes fire rebuilds)
- Centralize tray item ids in one constants module to prevent collisions
When it happens
Trigger: create/update tray while Secure Input is active (e.g. a password prompt grabbed it) and MenuItem::with_id(app, "secure_input_warning", label, true, None) errors — id collision with another item, or a rebuild racing shutdown.
Common situations: Tray icon refresh (theme change triggers refresh_tray_icon) racing quit; forks adding items that reuse the id; macOS menu services under load or during process teardown.
Related errors
- failed to create cancel item
- failed to create check updates item
- failed to create copy last transcript item
- failed to create menu
- failed to create model item
AI-assisted analysis of cjpais/Handy@fbd4e15fa1 (2026-08-17).
Data as JSON: /api/errors/b626a18e144d7c6b.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/tray.rs:202
}
/// Gets the appropriate icon path for the given theme and state.
///
/// `warning` overlays a badge on the idle icon while keyboard shortcuts are
/// blocked (macOS Secure Input); recording/transcribing states keep their
/// normal icons so in-flight activity stays recognizable.
pub fn get_icon_path(theme: AppTheme, state: TrayIconState, warning: bool) -> &'static str {
if warning && state == TrayIconState::Idle {
return match theme {
AppTheme::Dark => "resources/tray_idle_warning.png",
AppTheme::Light => "resources/tray_idle_warning_dark.png",
// Linux never sets the warning flag (Secure Input is macOS-only),
// but fall back to the normal icon just in case.
AppTheme::Colored => "resources/handy.png",
};
}
match (theme, state) {
// Dark theme uses light icons
(AppTheme::Dark, TrayIconState::Idle) => "resources/tray_idle.png",
(AppTheme::Dark, TrayIconState::Recording) => "resources/tray_recording.png",
(AppTheme::Dark, TrayIconState::Transcribing) => "resources/tray_transcribing.png",
// Light theme uses dark icons
(AppTheme::Light, TrayIconState::Idle) => "resources/tray_idle_dark.png",
(AppTheme::Light, TrayIconState::Recording) => "resources/tray_recording_dark.png",
(AppTheme::Light, TrayIconState::Transcribing) => "resources/tray_transcribing_dark.png",
// Colored theme uses pink icons (for Linux)
(AppTheme::Colored, TrayIconState::Idle) => "resources/handy.png",
(AppTheme::Colored, TrayIconState::Recording) => "resources/recording.png",
(AppTheme::Colored, TrayIconState::Transcribing) => "resources/transcribing.png",
}
}
/// Sets the recording state shown by the tray (icon + Cancel/model menu).
pub fn set_tray_state(app: &AppHandle, state: TrayIconState) {
sync_tray_with(app, |inner| inner.icon_state = state);
}View on GitHub (pinned to fbd4e15fa1)