{"record":{"id":"fc3b42d3f06aca47","repo":"Hmbown/CodeWhale","slug":"terminal-input-pump-did-not-pause-before-child-terminal","errorCode":null,"errorMessage":"terminal input pump did not pause before child terminal handoff","messagePattern":"terminal input pump did not pause before child terminal handoff","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/ui/terminal_input.rs","lineNumber":122,"sourceCode":"/// not run the child, because that is exactly the keystroke-splitting state\n/// this guards against. A process with no pump published (tests, non-TUI\n/// callers) has nothing to pause and succeeds with an inert guard, matching\n/// [`TerminalInputPump::pause_for_child_terminal`]'s `handle.is_none()` case.\npub(crate) fn pause_terminal_input_for_child() -> io::Result<ChildTerminalInputPause> {\n    let Some(gate) = CHILD_TERMINAL_GATE\n        .lock()\n        .ok()\n        .and_then(|gate| gate.clone())\n    else {\n        return Ok(ChildTerminalInputPause { gate: None });\n    };\n    gate.paused.store(true, Ordering::Release);\n    let deadline = Instant::now() + TERMINAL_INPUT_CHILD_PAUSE_TIMEOUT;\n    while !gate.paused_ack.load(Ordering::Acquire) {\n        if Instant::now() >= deadline {\n            gate.paused_ack.store(false, Ordering::Release);\n            gate.paused.store(false, Ordering::Release);\n            return Err(io::Error::new(\n                io::ErrorKind::TimedOut,\n                \"terminal input pump did not pause before child terminal handoff\",\n            ));\n        }\n        // Blocking-call convention (#6149): a bounded retry, capped by\n        // `TERMINAL_INPUT_CHILD_PAUSE_TIMEOUT`, in a synchronous API whose\n        // caller is about to block this very thread on a foreground editor\n        // for as long as the user keeps it open. `tokio::time` is not\n        // reachable from here and would not change what the thread does.\n        thread::sleep(TERMINAL_INPUT_CHILD_PAUSE_POLL_INTERVAL);\n    }\n    Ok(ChildTerminalInputPause { gate: Some(gate) })\n}\n\nimpl Drop for ChildTerminalInputPause {\n    fn drop(&mut self) {\n        if let Some(gate) = self.gate.take() {\n            gate.paused_ack.store(false, Ordering::Release);","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/ui/terminal_input.rs#L104-L140","documentation":"pause_terminal_input_for_child_terminal sets a `paused` flag and waits (bounded by TERMINAL_INPUT_CHILD_PAUSE_TIMEOUT) for the input-pump thread to acknowledge via `paused_ack`. If the ack never arrives before the deadline, it rolls both flags back and returns ErrorKind::TimedOut, refusing the child-terminal handoff so the pump is never left in a half-paused state.","triggerScenarios":"Calling pause_terminal_input_for_child_terminal when the pump thread is stuck, busy, or dead and does not observe `paused` within the timeout (tested by the refusal test).","commonSituations":"Pump thread blocked on a read that never yields, thread already exited, extreme system load delaying the poll loop past the deadline.","solutions":["Verify the terminal input pump thread is alive and processing gates before handing off to a child terminal.","Increase TERMINAL_INPUT_CHILD_PAUSE_TIMEOUT if the platform legitimately needs longer to ack.","Retry the handoff after confirming pump health."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// before handoff\nif !terminal_pump_is_alive() { restart_pump()?; }","typeGuard":null,"tryCatchPattern":"match pause_terminal_input_for_child_terminal(&gate) {\n    Err(e) if e.kind() == io::ErrorKind::TimedOut => abort_handoff_gracefully(),\n    Ok(guard) => run_child_terminal(guard),\n    Err(e) => return Err(e),\n}","preventionTips":["Keep the pump poll loop responsive (no long blocking work).","Health-check the pump thread before handoff.","Size TERMINAL_INPUT_CHILD_PAUSE_TIMEOUT to the slowest supported platform."],"tags":["terminal","timeout","concurrency"],"backgroundTag":"request-timeout","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"}