{"record":{"id":"ca938964ebbfa866","repo":"libnyanpasu/clash-nyanpasu","slug":"app-handle-is-none","errorCode":null,"errorMessage":"app handle is none","messagePattern":"app handle is none","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/core/hotkey.rs","lineNumber":254,"sourceCode":"        // Validate super key requirement\n        if !Self::validate_super_key(hotkey) {\n            bail!(\"{}\", t!(\"hotkey_error.missing_super_key\"));\n        }\n        Ok(())\n    }\n\n    /// Check if the hotkey contains a super key modifier (case-insensitive)\n    pub fn validate_super_key(hotkey: &str) -> bool {\n        let hotkey_lower = hotkey.to_lowercase();\n        SUPER_KEYS\n            .iter()\n            .any(|key| hotkey_lower.contains(&key.to_lowercase()))\n    }\n\n    fn register(&self, hotkey: &str, func: &str) -> Result<()> {\n        let app_handle = self.app_handle.lock();\n        if app_handle.is_none() {\n            bail!(\"app handle is none\");\n        }\n        let manager = app_handle.as_ref().unwrap().global_shortcut();\n\n        if manager.is_registered(hotkey) {\n            manager.unregister(hotkey)?;\n        }\n\n        let hotkey_func: HotkeyFunc = func.trim().parse()?;\n\n        manager.on_shortcut(hotkey, move |app_handle, hotkey, ev| {\n            if let ShortcutState::Pressed = ev.state {\n                tracing::info!(\"hotkey pressed: {}\", hotkey);\n                hotkey_func.execute(app_handle);\n            }\n        })?;\n\n        log::info!(target: \"app\", \"register hotkey {hotkey} {func}\");\n        Ok(())","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/hotkey.rs#L236-L272","documentation":"Hotkey::register looks up the stored Tauri AppHandle to access global_shortcut manager; if it is None it bails with 'app handle is none'. This means hotkey registration was attempted before the Tauri app handle was stored in the Hotkey struct (or after teardown). It's a lifecycle invariant violation inside the hotkey service.","triggerScenarios":"update() re-registering all hotkeys before app setup completes or after the handle was cleared; enabling hotkeys in config applied during early startup before the handle exists.","commonSituations":"Config with enable_hotkey loaded before Tauri setup finished; tests constructing Hotkey without an app handle; restart/teardown races.","solutions":["Ensure update()/register() is only called after the app handle is injected (post-setup initialization order)","Queue pending registrations and flush them once the handle becomes available","Migrate per actor-migration: own registration in a HotkeyActor whose startup arguments include the shortcut adapter, removing the None case","Improve the error message to mention hotkey registration before app initialization"],"exampleFix":"// before\nlet app_handle = self.app_handle.lock();\nif app_handle.is_none() {\n    bail!(\"app handle is none\");\n}\n// after\nlet Some(app_handle) = self.app_handle.lock().as_ref() else {\n    log::warn!(\"hotkey registration skipped: app handle not ready\");\n    return Ok(());\n};","handlingStrategy":"try-catch","validationCode":"fn hotkeys_ready(h: &Hotkey) -> bool {\n    h.app_handle.lock().is_some()\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = hotkey.update() {\n    log::warn!(\"hotkey registration deferred: {e}\");\n    pending_registration.store(true, Ordering::Relaxed);\n}","preventionTips":["Initialize hotkeys only after app handle injection","Queue registrations until the handle is available","Inject a shortcut adapter at startup (HotkeyActor) instead of an Option handle"],"tags":["hotkey","tauri","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"}