{"record":{"id":"9653e7d219bb5f15","repo":"Hmbown/CodeWhale","slug":"terminal-host-window-owner-changed","errorCode":null,"errorMessage":"terminal host window owner changed","messagePattern":"terminal host window owner changed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/window_control.rs","lineNumber":122,"sourceCode":"                GetWindowThreadProcessId(hwnd, Some(&mut owner_pid));\n            }\n            if owner_pid == 0 {\n                bail!(\"terminal host window no longer exists\");\n            }\n            Ok(Self {\n                handle: hwnd.0 as isize,\n                owner_pid,\n            })\n        }\n\n        fn hwnd(self) -> Result<HWND> {\n            let hwnd = HWND(self.handle as *mut _);\n            let mut owner_pid = 0;\n            unsafe {\n                GetWindowThreadProcessId(hwnd, Some(&mut owner_pid));\n            }\n            if owner_pid != self.owner_pid {\n                bail!(\"terminal host window owner changed\");\n            }\n            Ok(hwnd)\n        }\n    }\n\n    #[derive(Clone, Copy)]\n    struct Observation {\n        rect: RECT,\n        topmost: bool,\n        maximized: bool,\n    }\n\n    impl Observation {\n        fn matches(self, target: Self) -> bool {\n            self.topmost == target.topmost\n                && self.maximized == target.maximized\n                && (target.maximized || self.rect == target.rect)\n        }","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/window_control.rs#L104-L140","documentation":"WindowControl::hwnd validates that the cached window handle still belongs to the same owning process recorded at capture time. GetWindowThreadProcessId returning a different (or zero) pid means the original host window was destroyed and possibly replaced, so the cached handle is invalid and this error is raised.","triggerScenarios":"Calling hwnd (e.g. from observe during toggle_pin or wait_for) when the current owner pid of the cached HWND differs from self.owner_pid — the terminal host process restarted or the window was destroyed.","commonSituations":"Windows Terminal/conhost restarted while the TUI ran; the user closed and reopened the terminal; a shell integration reset the console window between capture and a later toggle.","solutions":["Re-run capture to bind to the new host window and update the cached handle/owner pid, then retry the operation.","Check that the terminal that started the TUI is still the active window; restart the TUI in the new terminal if not.","Guard toggle flows so they re-validate the host (capture) if hwnd reports an owner change."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match host.hwnd() {\n    Err(e) if e.to_string().contains(\"owner changed\") => { state.host = Some(WindowControl::capture()?); retry(); }\n    other => other?,\n}","preventionTips":["Validate owner pid before every use of a cached HWND (as hwnd() does).","Re-capture after any terminal restart or long idle period.","Don't cache window handles across operations that can outlive the host process.","Fail soft: degrade to non-pinned mode when the host is gone."],"tags":["windows","win32","terminal-window","stale-handle"],"backgroundTag":"invalid-state-transition","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}