{"record":{"id":"5d5895e7f015fae3","repo":"cjpais/Handy","slug":"failed-to-create-model-item","errorCode":null,"errorMessage":"failed to create model item","messagePattern":"failed to create model item","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/tray.rs","lineNumber":267,"sourceCode":"\n    // Record intent and claim a sequence number in one critical section, so\n    // sequence order == the order in which state changes were requested.\n    let (seq, icon_state) = {\n        let mut inner = state.lock();\n        update(&mut inner);\n        inner.next_seq += 1;\n        (inner.next_seq, inner.icon_state)\n    };\n\n    // Tray not built yet (early secure-input monitor callbacks). The intent\n    // is kept and picked up by the first sync after the tray exists.\n    if app.try_state::<TrayIcon>().is_none() {\n        return;\n    }\n\n    let desired = compute_desired(app, icon_state);\n\n    // Decode the icon off the main thread, once per path, outside the lock.\n    let needs_icon = !state.lock().icons.contains_key(desired.icon_path);\n    let loaded_icon = if needs_icon {\n        match load_tray_icon(\n            app.path()\n                .resolve(desired.icon_path, tauri::path::BaseDirectory::Resource),\n        ) {\n            Ok(image) => Some(image),\n            Err(err) => {\n                error!(\"Failed to load tray icon '{}': {err}\", desired.icon_path);\n                None\n            }\n        }\n    } else {\n        None\n    };\n\n    let schedule = {\n        let mut inner = state.lock();","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/cjpais/Handy/blob/fbd4e15fa14a721c66c57006ae110428b9e255b3/src-tauri/src/tray.rs#L249-L285","documentation":"Inside the model submenu loop, one CheckMenuItem::with_id is created per downloaded model with the id format \"model_select:{model.id}\". The .expect panics if the native backend rejects any single item. Beyond the shared menu-manager failures, this per-item construction is the one most sensitive to item-id hygiene: duplicate model ids (two ModelManager entries sharing an id) produce duplicate menu ids in the same submenu, which breaks event routing and can error in some muda backends.","triggerScenarios":"Having several models downloaded (each gets an item) on a Linux system without a working DBusMenu registrar; a corrupt/duplicated models list from ModelManager::get_available_models where two entries share an id, producing identical \"model_select:{id}\" ids in one submenu; rebuild during shutdown.","commonSituations":"Power users with many downloaded Whisper variants; model metadata files duplicated during an interrupted download; headless or tray-less test environments.","solutions":["Confirm the panic reproduces for any number of models — if it only happens with many items, audit get_available_models for duplicate ids","Deduplicate/filter model entries by id before building the submenu: downloaded.dedup_by(|a, b| a.id == b.id)","Propagate the error instead of .expect: skip the failing item and log, so one bad model does not kill the tray","Ensure a system tray provider is running on Linux when testing"],"exampleFix":"// before\nlet item = CheckMenuItem::with_id(app, &item_id, &model.name, true, is_active, None::<&str>)\n    .expect(\"failed to create model item\");\nlet _ = submenu.append(&item);\n\n// after\nif let Ok(item) = CheckMenuItem::with_id(app, &item_id, &model.name, true, is_active, None::<&str>) {\n    let _ = submenu.append(&item);\n} else {\n    warn!(\"Skipping model item for {} ({item_id})\", model.name);\n}","handlingStrategy":"fallback","validationCode":"// Deduplicate model ids before building submenu items\nlet mut seen = std::collections::HashSet::new();\ndownloaded.retain(|m| seen.insert(m.id.clone()));","typeGuard":null,"tryCatchPattern":"if let Ok(item) = CheckMenuItem::with_id(app, &item_id, &model.name, true, is_active, None::<&str>) {\n    let _ = submenu.append(&item);\n} else {\n    warn!(\"skipping model item {item_id}\");\n}","preventionTips":["Validate ModelManager output: ids must be unique and non-empty before they become menu ids","Per-item failures should skip the item, not panic the whole tray rebuild","Keep item id formats stable ('model_select:{id}') because the event handler routes on them"],"tags":["tauri","tray","muda","check-menu-item","rust","panic"],"backgroundTag":"tauri-tray-menu-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"}