{"record":{"id":"261c0b63e9db8ec1","repo":"libnyanpasu/clash-nyanpasu","slug":"app-handle-is-not-exist","errorCode":null,"errorMessage":"app_handle is not exist","messagePattern":"app_handle is not exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/tauri/src/core/handle.rs","lineNumber":100,"sourceCode":"        // 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":82,"sourceCodeEnd":107,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/handle.rs#L82-L107","documentation":"Handle::emit sends a serialized event to the Tauri frontend via the stored global AppHandle. If the AppHandle was never set (app not initialized) it bails with 'app_handle is not exist'. This is a lifecycle/ordering error: events are being emitted before the Tauri application handle is registered in the global singleton.","triggerScenarios":"Calling Handle::emit(event, payload) during early startup before setup() stores the app handle; background tasks (log watchers, core callbacks) firing events at startup or teardown after handle removal.","commonSituations":"Config/log listeners started before Tauri setup completes; tests exercising event-emitting code without a Tauri runtime; shutdown races where a task emits after the app dropped.","solutions":["Delay event-producing tasks until after Tauri setup stores the app handle","Make emit fail-soft: log a warning and return Ok when no handle is registered, since UI events are non-critical","Replace Handle::global() with an injected UiEventSink adapter so the dependency is explicit and testable","Use a OnceLock/watch channel so emitters wait for handle availability instead of failing"],"exampleFix":"// before\nbail!(\"app_handle is not exist\");\n// after\nlet Some(app_handle) = app_handle.as_ref() else {\n    log::debug!(\"emit skipped, app_handle not ready: {event}\");\n    return Ok(());\n};","handlingStrategy":"try-catch","validationCode":"fn events_ready(handle: &Handle) -> bool {\n    handle.app_handle.lock().is_some()\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = Handle::emit(\"config-changed\", payload) {\n    log::debug!(\"event emit skipped: {e}\");\n}","preventionTips":["Start event-producing tasks after app initialization","Make UI events non-fatal","Inject a UiEventSink instead of using the global Handle"],"tags":["tauri","events","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"}