cjpais/Handy · error
failed to create check updates item
Error message
failed to create check updates item
What it means
Panic when MenuItem::with_id fails to create the 'check_updates' item. Its enabled state reflects settings.update_checks_enabled, but that flag does not affect creation; failure causes are the same muda family — duplicated id, invalid label/id, or a tray rebuild racing app teardown. Because strings come from i18n, an unexpected label issue (e.g. interior NUL on some backends) is theoretically possible but not observed.
Solutions
- Keep 'check_updates' unique within the menu build
- Skip tray rebuilds during shutdown
- Degrade gracefully (log and build the menu without the item) since update checks also surface in the settings UI
Example fix
// before
let check_updates_i = MenuItem::with_id(
app,
"check_updates",
&strings.check_updates,
settings.update_checks_enabled,
None::<&str>,
)
.expect("failed to create check updates item");
// after — include the id and settings state in diagnostics
let check_updates_i = MenuItem::with_id(
app,
"check_updates",
&strings.check_updates,
settings.update_checks_enabled,
None::<&str>,
)
.unwrap_or_else(|e| panic!("failed to create check updates item (id='check_updates'): {e}")); Defensive patterns
Strategy: fallback
Try / catch
let check_updates_i =
match MenuItem::with_id(app, "check_updates", &strings.check_updates, settings.update_checks_enabled, None::<&str>) {
Ok(item) => Some(item),
Err(e) => {
log::warn!("check-updates tray item skipped: {e}");
None // update checks remain available in the settings UI
}
}; Prevention
- Keep tray ids unique and defined centrally
- Remember update checks have a settings-UI path, so the tray item is safely skippable
- Coalesce frequent tray rebuilds and stop them at shutdown
When it happens
Trigger: Tray menu build where MenuItem::with_id(app, "check_updates", strings.check_updates, settings.update_checks_enabled, None) errors — id collision or teardown race.
Common situations: Theme-change rebuilds racing quit; forks adding a second updates entry with the same id; localized label regressions from the i18n pipeline.
Related errors
- failed to create copy last transcript item
- failed to create settings item
- failed to create version item
- failed to create cancel item
- failed to create menu
AI-assisted analysis of cjpais/Handy@fbd4e15fa1 (2026-08-17).
Data as JSON: /api/errors/2e0932be393f3b12.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/tray.rs:230
(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);
}
/// Re-syncs the tray after something other than the recording state changed
/// (theme, Secure Input warning). The recording state itself is preserved.
pub fn refresh_tray_icon(app: &AppHandle) {
sync_tray(app);
}
/// Re-syncs the tray after something the menu depends on changed (model
/// list/selection/loaded state, language, settings).
pub fn update_tray_menu(app: &AppHandle) {
sync_tray(app);
}
/// Records the current desired tray state and schedules one apply on the main
/// thread (or lets an already-pending apply pick it up). Never blocks on the
/// main thread.
///
/// The snapshot (settings, model list, loaded state) is computed on the
/// *calling* thread on purpose: the main-thread applier must not take manager
/// locks that a worker may hold across slow work (see #1716).
pub fn sync_tray(app: &AppHandle) {
sync_tray_with(app, |_| {});
}
fn sync_tray_with(app: &AppHandle, update: impl FnOnce(&mut TrayInner)) {
let Some(state) = app.try_state::<TrayState>() else {
return;
};View on GitHub (pinned to fbd4e15fa1)