{"record":{"id":"61757d1c69bb92d9","repo":"wezterm/wezterm","slug":"terminalshim-waker-called","errorCode":null,"errorMessage":"TerminalShim::waker called!?","messagePattern":"TerminalShim::waker called!\\?","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mux/src/ssh.rs","lineNumber":567,"sourceCode":"                    self.parser\n                        .parse(&buf[0..n], |evt| input_queue.push_back(evt), n == buf.len());\n                    return Ok(self.input_queue.pop_front());\n                } else {\n                    let size = *self.size.lock().unwrap();\n                    if starting_size != size {\n                        return Ok(Some(InputEvent::Resized {\n                            cols: size.cols as usize,\n                            rows: size.rows as usize,\n                        }));\n                    }\n                }\n            }\n        }\n\n        fn waker(&self) -> TerminalWaker {\n            // TODO: TerminalWaker assumes that we're a SystemTerminal but that\n            // isn't the case here.\n            panic!(\"TerminalShim::waker called!?\");\n        }\n    }\n\n    let renderer = termwiz_funcs::new_wezterm_terminfo_renderer();\n    let mut shim = TerminalShim {\n        stdout: &mut StdoutShim {\n            stdout: stdout_write,\n            size: Arc::clone(&size),\n        },\n        size: Arc::clone(&size),\n        renderer,\n        stdin: &mut stdin_read,\n        parser: InputParser::new(),\n        input_queue: VecDeque::new(),\n    };\n\n    impl<'a> TerminalShim<'a> {\n        fn output_line(&mut self, s: &str) -> termwiz::Result<()> {","sourceCodeStart":549,"sourceCodeEnd":585,"githubUrl":"https://github.com/wezterm/wezterm/blob/3ff7522b9617ec920f7fbdb22b57e93d42d93ee9/mux/src/ssh.rs#L549-L585","documentation":"The scrollback-search handler's do_search helper failed at get_pane: the pane whose scrollback should be searched no longer exists, and the server returns ErrorResponse \"Error: no such pane {pane_id}\". The search itself runs on a spawned promise (sessionhandler.rs:527-533), but the lookup error still flows back through send_response to the client that opened the search.","triggerScenarios":"The GUI search overlay (default CTRL+SHIFT+F) being used in a tab whose process exits while the overlay is open (or between keystrokes); a programmatic search_scrollback call with a pane_id from a stale ListPanes snapshot.","commonSituations":"Searching a tab where a short-lived command already exited; remote/tls domains where the pane was pruned after the client cached its id; scripted capture-and-search racing pane teardown.","solutions":["Dismiss the search on pane exit: subscribe to PaneClosed/WindowClosed notifications and close the overlay instead of issuing the RPC","For programmatic search, resolve the pane id from a fresh list immediately before searching","If you need the scrollback of a pane that may die, copy/get the lines while it is alive rather than searching afterwards"],"exampleFix":"// before: search with a possibly stale id\nlet res = conn.search_scrollback(SearchScrollbackRequest { pane_id, pattern, range, limit }).await?;\n\n// after: validate liveness first, degrade gracefully\nif !pane_exists(&conn, pane_id).await? {\n    anyhow::bail!(\"pane {pane_id} closed before search\");\n}\nlet res = conn.search_scrollback(SearchScrollbackRequest { pane_id, pattern, range, limit }).await?;","handlingStrategy":"validation","validationCode":"// Before searching scrollback, confirm the pane is alive\nif !pane_exists(&conn, pane_id).await? {\n    anyhow::bail!(\"pane {pane_id} closed; aborting scrollback search\");\n}\nconn.search_scrollback(SearchScrollbackRequest { pane_id, pattern, range, limit }).await?;","typeGuard":"fn pane_alive(res: &ListPanesResponse, pane_id: PaneId) -> bool {\n    fn walk(n: &PaneNode, id: PaneId) -> bool {\n        match n {\n            PaneNode::Leaf(e) => e.pane_id == id,\n            PaneNode::Split { left, right, .. } => walk(left, id) || walk(right, id),\n            PaneNode::Empty => false,\n        }\n    }\n    res.tabs.iter().any(|t| walk(t, pane_id))\n}","tryCatchPattern":"match conn.search_scrollback(SearchScrollbackRequest { pane_id, pattern, range, limit }).await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"no such pane\") => {\n        // pane died mid-search: close the search UI / return empty results\n        SearchScrollbackResponse { results: vec![] }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Dismiss search overlays when the pane closes (listen for PaneClosed) instead of letting them keep querying","Capture scrollback with get-lines while the pane lives rather than searching after death","For incremental search, tolerate per-keystroke failures when the underlying pane exits"],"tags":["wezterm","mux","rpc","search","scrollback","pane","stale-id"],"backgroundTag":"stale-pane-id","analyzedSha":"3ff7522b9617ec920f7fbdb22b57e93d42d93ee9","analyzedAt":"2026-08-20T04:47:31.434Z","contentChangedAt":"2026-08-20T04:47:31.434Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}