{"record":{"id":"5c981e641100e409","repo":"libnyanpasu/clash-nyanpasu","slug":"update-systray-unhandled-error","errorCode":null,"errorMessage":"update_systray unhandled error","messagePattern":"update_systray unhandled error","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/core/handle.rs","lineNumber":91,"sourceCode":"            log_err!(window.emit(NOTIFY_MESSAGE_URI, message));\n        }\n    }\n\n    pub fn update_systray() -> Result<()> {\n        // let app_handle = Self::global().app_handle.lock();\n        // if app_handle.is_none() {\n        //     bail!(\"update_systray unhandled error\");\n        // }\n        // Tray::update_systray(app_handle.as_ref().unwrap())?;\n        Handle::emit(\"update_systray\", ())?;\n        Ok(())\n    }\n\n    /// update the system tray state\n    pub fn update_systray_part() -> Result<()> {\n        let app_handle = Self::global().app_handle.lock();\n        if app_handle.is_none() {\n            bail!(\"update_systray unhandled error\");\n        }\n        Tray::update_part(app_handle.as_ref().unwrap())?;\n        Ok(())\n    }\n\n    pub fn emit<S: Serialize + Clone>(event: &str, payload: S) -> Result<()> {\n        let app_handle = Self::global().app_handle.lock();\n        if app_handle.is_none() {\n            bail!(\"app_handle is not exist\");\n        }\n\n        app_handle.as_ref().unwrap().emit(event, payload)?;\n        Ok(())\n    }\n}\n","sourceCodeStart":73,"sourceCodeEnd":107,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/handle.rs#L73-L107","documentation":"update_systray_part is a static method on the Handle global that reads the stored Tauri AppHandle to refresh the system tray. If the app handle was never stored (app not yet initialized) the method bails with this generic, unhelpful message instead of a typed error. It is an internal invariant violation: tray updates were requested before the app handle exists.","triggerScenarios":"Calling Handle::update_systray_part() before setup() stores the AppHandle — e.g. during early startup, config import, or a command racing app initialization; also after a failed shutdown cleared it.","commonSituations":"Tray update triggered by a config-change event that fires before Tauri setup completes; tests invoking core logic without a Tauri runtime; unit tests calling Handle::global() with no app handle registered.","solutions":["Ensure the caller only invokes tray updates after app setup completes (sequence initialization before event emission)","Make the method a no-op-with-warning when the handle is missing instead of an error, since tray refresh is a UI side effect","Migrate per the actor-migration plan: replace Handle::global() with a UiEventSink/TauriUiEventSink adapter injected at the composition root","Improve the message to state that the app handle is not initialized"],"exampleFix":"// before\nbail!(\"update_systray unhandled error\");\n// after\nlet Some(app_handle) = app_handle.as_ref() else {\n    log::warn!(\"update_systray skipped: app handle not initialized yet\");\n    return Ok(());\n};","handlingStrategy":"try-catch","validationCode":"// caller-side guard\nfn tray_ready(handle: &Handle) -> bool {\n    handle.app_handle.lock().is_some()\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = Handle::update_systray_part() {\n    log::warn!(\"systray update skipped: {e}\");\n}","preventionTips":["Only trigger tray updates after Tauri setup stores the app handle","Make tray refresh fail-soft in background flows","Replace global Handle with an injected UI adapter"],"tags":["tauri","systray","initialization"],"backgroundTag":"internal-invariant-violation","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}