{"record":{"id":"03db4d41d244b3c0","repo":"Hmbown/CodeWhale","slug":"a-window-change-is-already-in-progress","errorCode":null,"errorMessage":"a window change is already in progress","messagePattern":"a window change is already in progress","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/tui/src/tui/window_control.rs","lineNumber":190,"sourceCode":"            }\n            if Instant::now() >= deadline {\n                return Ok(false);\n            }\n            std::thread::sleep(Duration::from_millis(15));\n        }\n    }\n\n    pub(super) fn start_toggle(\n        completion_tx: Option<tokio::sync::mpsc::Sender<crate::tui::app::DispatchApplyFn>>,\n    ) -> Result<()> {\n        // Reserve delivery before changing the window. A headless test App has\n        // no mailbox and cannot accidentally manipulate its real terminal.\n        let permit = completion_tx\n            .context(\"window completion mailbox is unavailable\")?\n            .try_reserve_owned()\n            .context(\"window completion mailbox is full or closed\")?;\n        BUSY.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)\n            .map_err(|_| anyhow::anyhow!(\"a window change is already in progress\"))?;\n        let busy = BusyGuard;\n        // Foreground selection belongs to the user's action, before dispatch;\n        // the worker must not pick a different window after focus changes.\n        let host = HostWindow::capture()?;\n        std::thread::Builder::new()\n            .name(\"window-pin\".into())\n            .spawn(move || {\n                let result = std::panic::catch_unwind(|| toggle_pin(host))\n                    .unwrap_or_else(|_| Err(anyhow::anyhow!(\"window worker panicked\")));\n                let apply: crate::tui::app::DispatchApplyFn = Box::new(move |app, _, _| {\n                    super::show_result(app, result);\n                    Ok(())\n                });\n                permit.send(apply);\n                drop(busy);\n            })?;\n        Ok(())\n    }","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/window_control.rs#L172-L208","documentation":"start_toggle serializes window pin/toggle operations with an atomic BUSY flag. If compare_exchange(false, true) fails, another window change is still in flight, so the new request is rejected instead of interleaving with it. This protects HostWindow::capture/dispatch ordering from concurrent toggles.","triggerScenarios":"Calling start_toggle twice before the first window-pin worker thread completes (the worker clears BUSY via BusyGuard on exit); rapid repeated keybindings or programmatic toggles issued back-to-back.","commonSituations":"User mashes the pin hotkey; an automation/test fires toggles faster than the worker applies them; a prior toggle's worker thread is stuck waiting on the completion mailbox.","solutions":["Retry after a short delay, once the in-flight change completes (BUSY is released by the worker's BusyGuard).","Debounce/deduplicate toggle keypresses in the UI layer so only one request is in flight.","If toggles consistently stay busy, investigate a wedged window-pin worker thread (check for a missing permit.send or a hang in toggle_pin)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// poll before issuing the toggle\nif !window_control_is_idle() {\n    return; // a change is already in progress\n}","typeGuard":null,"tryCatchPattern":"match start_toggle() {\n    Err(e) if e.to_string().contains(\"already in progress\") => {\n        // schedule a retry after the in-flight change completes\n        schedule_retry(Duration::from_millis(100));\n    }\n    Err(e) => report(e),\n    Ok(()) => {}\n}","preventionTips":["Debounce the toggle hotkey so repeat presses are ignored while busy.","Serialize toggles through a single UI command channel.","Treat busy as normal backpressure, not a fault; retry shortly."],"tags":["concurrency","busy-state","window-control","tui"],"backgroundTag":"conflicting-config-options","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"}