{"record":{"id":"7190eae7243c3339","repo":"libnyanpasu/clash-nyanpasu","slug":"failed-to-retain-window","errorCode":null,"errorMessage":"failed to retain window","messagePattern":"failed to retain window","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/window.rs","lineNumber":969,"sourceCode":"\n    #[derive(Debug, Clone)]\n    struct WindowState {\n        window: WebviewWindow<tauri::Wry>,\n        traffic_lights_pos: Position,\n    }\n\n    impl WindowState {\n        fn new(window: WebviewWindow<tauri::Wry>, traffic_lights_pos: Position) -> Self {\n            Self {\n                window,\n                traffic_lights_pos,\n            }\n        }\n\n        fn with_ns_window<T>(&self, func: impl FnOnce(Retained<NSWindow>) -> T) -> T {\n            let ns_window = self.window.ns_window().expect(\"window not found\");\n            let ns_window = unsafe { Retained::retain_autoreleased(ns_window as *mut NSWindow) }\n                .expect(\"failed to retain window\");\n            func(ns_window)\n        }\n\n        fn apply_traffic_lights_pos(&self) {\n            self.with_ns_window(|win| {\n                set_traffic_lights_pos(win, self.traffic_lights_pos)\n                    .expect(\"failed to set traffic lights pos\");\n            });\n        }\n    }\n\n    #[derive(Debug)]\n    struct TrafficLightsWindowDelegateIvars {\n        app_box: WindowState,\n        super_class: Retained<ProtocolObject<dyn NSWindowDelegate>>,\n    }\n\n    const WINDOW_DID_ENTER_FULL_SCREEN: &str = \"internal:://window-did-enter-full-screen\";","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/window.rs#L951-L987","documentation":"After obtaining the raw NSWindow pointer, the code re-anchors it into an objc2 `Retained<NSWindow>` via `retain_autoreleased`. The expect fires when the pointer is not a valid autoreleased `NSWindow` (nil, already deallocated, or wrong type), so objc2 refuses to build a safe reference. It indicates the window handle became invalid between the `ns_window()` call and the retain, or the unsafe cast is wrong.","triggerScenarios":"Calling `with_ns_window` while the NSWindow is being torn down (close callback racing deallocation), or the `ns_window()` return being a non-NSWindow pointer so the `*mut NSWindow` cast is invalid.","commonSituations":"App shutdown or window close triggering `apply_traffic_lights_pos` on a half-deallocated window; calling from a non-main thread where AppKit objects are invalid; Tauri/objc2 version mismatch changing raw-handle semantics.","solutions":["Ensure the call runs on the main thread and the window is confirmed alive before the retain.","Check `retain_autoreleased` returns Some and bail gracefully (log + skip) instead of expecting.","Pin/align tauri and objc2 crate versions so `ns_window()`'s autoreleased contract matches the code's assumption.","Move traffic-light updates out of close-time callbacks where the window may already be deallocating."],"exampleFix":"// before\nlet ns_window = unsafe { Retained::retain_autoreleased(ns_window as *mut NSWindow) }\n    .expect(\"failed to retain window\");\n// after\nlet Some(ns_window) = (unsafe {\n    Retained::retain_autoreleased(ns_window as *mut NSWindow)\n}) else {\n    tracing::warn!(\"could not retain NSWindow; skipping\");\n    return Default::default();\n};","handlingStrategy":"fallback","validationCode":"// run only on main thread with a live window\nlet mtm = MainThreadMarker::new().expect(\"must run on main thread\");","typeGuard":"fn retainable_ns_window(ptr: *mut NSWindow) -> Option<Retained<NSWindow>> {\n    unsafe { Retained::retain_autoreleased(ptr) }\n}","tryCatchPattern":"let Some(win) = (unsafe { Retained::retain_autoreleased(p as *mut NSWindow) }) else {\n    tracing::warn!(\"retain_autoreleased failed; skipping traffic lights\");\n    return Default::default();\n};","preventionTips":["Align tauri/objc2 versions so ns_window() guarantees an autoreleased pointer","Avoid touching the NSWindow during deallocation/close callbacks","Always call via main-thread-only helpers"],"tags":["macos","objc2","memory-safety","panic"],"backgroundTag":"objc-retain-failed","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"}