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

  1. Ensure the id 'secure_input_warning' is used exactly once per menu build
  2. Guard tray rebuild paths against app teardown (check the app is still running before rebuilding)
  3. 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

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


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)