{"record":{"id":"07a33fcb0a32c458","repo":"cjpais/Handy","slug":"failed-to-create-settings-item","errorCode":null,"errorMessage":"failed to create settings item","messagePattern":"failed to create settings item","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/tray.rs","lineNumber":222,"sourceCode":"        (AppTheme::Dark, TrayIconState::Recording) => \"resources/tray_recording.png\",\n        (AppTheme::Dark, TrayIconState::Transcribing) => \"resources/tray_transcribing.png\",\n        // Light theme uses dark icons\n        (AppTheme::Light, TrayIconState::Idle) => \"resources/tray_idle_dark.png\",\n        (AppTheme::Light, TrayIconState::Recording) => \"resources/tray_recording_dark.png\",\n        (AppTheme::Light, TrayIconState::Transcribing) => \"resources/tray_transcribing_dark.png\",\n        // Colored theme uses pink icons (for Linux)\n        (AppTheme::Colored, TrayIconState::Idle) => \"resources/handy.png\",\n        (AppTheme::Colored, TrayIconState::Recording) => \"resources/recording.png\",\n        (AppTheme::Colored, TrayIconState::Transcribing) => \"resources/transcribing.png\",\n    }\n}\n\n/// Sets the recording state shown by the tray (icon + Cancel/model menu).\npub fn set_tray_state(app: &AppHandle, state: TrayIconState) {\n    sync_tray_with(app, |inner| inner.icon_state = state);\n}\n\n/// Re-syncs the tray after something other than the recording state changed\n/// (theme, Secure Input warning). The recording state itself is preserved.\npub fn refresh_tray_icon(app: &AppHandle) {\n    sync_tray(app);\n}\n\n/// Re-syncs the tray after something the menu depends on changed (model\n/// list/selection/loaded state, language, settings).\npub fn update_tray_menu(app: &AppHandle) {\n    sync_tray(app);\n}\n\n/// Records the current desired tray state and schedules one apply on the main\n/// thread (or lets an already-pending apply pick it up). Never blocks on the\n/// main thread.\n///\n/// The snapshot (settings, model list, loaded state) is computed on the\n/// *calling* thread on purpose: the main-thread applier must not take manager\n/// locks that a worker may hold across slow work (see #1716).","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/cjpais/Handy/blob/fbd4e15fa14a721c66c57006ae110428b9e255b3/src-tauri/src/tray.rs#L204-L240","documentation":"Panic when MenuItem::with_id fails to create the 'settings' item, which unlike the version item registers a keyboard accelerator ('Cmd+,' on macOS, 'Ctrl+,' elsewhere). Accelerator registration is the extra failure mode here: an accelerator string that muda cannot parse or that the platform menu layer rejects makes item creation fail, in addition to the generic duplicated-id/teardown causes.","triggerScenarios":"Tray menu build where MenuItem::with_id(app, \"settings\", strings.settings, true, Some(accelerator)) errors — unparseable/unsupported accelerator for the platform, id collision, or rebuild racing shutdown.","commonSituations":"Forks changing the accelerator string to one the target platform's menu backend rejects; cross-platform builds where a platform-specific accelerator leaks to the wrong cfg branch; id reuse after menu restructuring.","solutions":["Verify the accelerator is valid for the platform (keep the cfg(target_os = \"macos\") split for Cmd/Ctrl)","Ensure 'settings' is unique within the menu build","Retry creation with None as the accelerator (menu still works; keyboard shortcut lost) before giving up","Include the accelerator in the panic message for fast diagnosis"],"exampleFix":"// before\nlet settings_i = MenuItem::with_id(\n    app,\n    \"settings\",\n    &strings.settings,\n    true,\n    settings_accelerator,\n)\n.expect(\"failed to create settings item\");\n\n// after — fall back to no accelerator rather than aborting\nlet settings_i = MenuItem::with_id(app, \"settings\", &strings.settings, true, settings_accelerator)\n    .or_else(|_| {\n        log::warn!(\"settings item created without accelerator\");\n        MenuItem::with_id(app, \"settings\", &strings.settings, true, None::<&str>)\n    })\n    .expect(\"failed to create settings item\");","handlingStrategy":"fallback","validationCode":"// Validate the accelerator string per platform before creating the item\n#[cfg(target_os = \"macos\")]\nconst SETTINGS_ACCEL: Option<&str> = Some(\"Cmd+,\");\n#[cfg(not(target_os = \"macos\"))]\nconst SETTINGS_ACCEL: Option<&str> = Some(\"Ctrl+,\");\n// keep accelerators compile-time constants per platform instead of runtime-computed strings","typeGuard":null,"tryCatchPattern":"let settings_i = MenuItem::with_id(app, \"settings\", &strings.settings, true, settings_accelerator)\n    .or_else(|_| {\n        log::warn!(\"settings item lost its accelerator; recreating without\");\n        MenuItem::with_id(app, \"settings\", &strings.settings, true, None::<&str>)\n    });","preventionTips":["Keep accelerator strings behind cfg(target_os) constants so a platform-invalid accelerator cannot leak across builds","Retry item creation without the accelerator before failing the whole tray","Include the accelerator value in diagnostics when creation fails"],"tags":["rust","tauri","tray","muda","menu","keyboard-accelerator"],"backgroundTag":"tray-menu-item-creation-failed","analyzedSha":"fbd4e15fa14a721c66c57006ae110428b9e255b3","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}