{"record":{"id":"14db1ed4a321093d","repo":"wezterm/wezterm","slug":"fontconfig-not-compiled-in","errorCode":null,"errorMessage":"fontconfig not compiled in","messagePattern":"fontconfig not compiled in","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"wezterm-font/src/locator/mod.rs","lineNumber":228,"sourceCode":"    ) -> anyhow::Result<Vec<ParsedFont>>;\n\n    fn enumerate_all_fonts(&self) -> anyhow::Result<Vec<ParsedFont>> {\n        Ok(vec![])\n    }\n\n    fn locate_fallback_for_codepoints(\n        &self,\n        codepoints: &[char],\n    ) -> anyhow::Result<Vec<ParsedFont>>;\n}\n\npub fn new_locator(locator: FontLocatorSelection) -> Arc<dyn FontLocator + Send + Sync> {\n    match locator {\n        FontLocatorSelection::FontConfig => {\n            #[cfg(all(unix, not(target_os = \"macos\")))]\n            return Arc::new(font_config::FontConfigFontLocator {});\n            #[cfg(not(all(unix, not(target_os = \"macos\"))))]\n            panic!(\"fontconfig not compiled in\");\n        }\n        FontLocatorSelection::CoreText => {\n            #[cfg(target_os = \"macos\")]\n            return Arc::new(core_text::CoreTextFontLocator {});\n            #[cfg(not(target_os = \"macos\"))]\n            panic!(\"CoreText not compiled in\");\n        }\n        FontLocatorSelection::Gdi => {\n            #[cfg(windows)]\n            return Arc::new(gdi::GdiFontLocator {});\n            #[cfg(not(windows))]\n            panic!(\"Gdi not compiled in\");\n        }\n        FontLocatorSelection::ConfigDirsOnly => Arc::new(NopSystemSource {}),\n    }\n}\n\nstruct NopSystemSource {}","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/wezterm/wezterm/blob/3ff7522b9617ec920f7fbdb22b57e93d42d93ee9/wezterm-font/src/locator/mod.rs#L210-L246","documentation":"The SetPaneZoomed handler's second lookup failed on containing_tab_id — an id supplied by the client in the PDU, not resolved by the server. The client derived it from its cached pane tree (a ListPanes snapshot); if the server-side tab was closed, moved, or renumbered-in-place since that snapshot, get_tab returns None and the server replies \"Error: no such tab {containing_tab_id}\". This is a client/server cache desync, not a missing pane.","triggerScenarios":"Toggling zoom after another client closed or reorganized the tab; GUI attached to a unix/tls/ssh mux domain lagging behind tab-close notifications; using tab ids from an older `wezterm cli list` while the tab list changed.","commonSituations":"Multi-client mux domains where tabs are opened/closed concurrently; flaky notification delivery on remote domains; scripts that cache (pane_id, tab_id) pairs across user-visible delays.","solutions":["Re-sync ids from a fresh ListPanes (or `wezterm cli list`) immediately before calling set_zoomed — the PaneEntry rows carry both pane_id and tab_id","Act on mux notifications (TabClosed/WindowClosed) to invalidate cached tab ids so zoom calls always use current ids","Treat the error as transient: re-resolve and retry once; if the tab is truly gone, abandon the operation"],"exampleFix":"# before: tab id captured earlier in the script\nwezterm cli ... # uses TAB=5 -> Error: no such tab 5\n\n# after: derive tab id from the live pane at call time\nTAB=$(wezterm cli list --format json | jq \"[.[] | select(.pane_id==$PANE)][0].tab_id\")\n[ -n \"$TAB\" ] && echo \"zoom pane $PANE in tab $TAB\"","handlingStrategy":"validation","validationCode":"// Re-derive containing_tab_id from a fresh snapshot instead of trusting the cache\nlet panes = conn.list_panes().await?;\nlet entry = find_entry(&panes, pane_id)\n    .ok_or_else(|| anyhow!(\"pane {pane_id} gone\"))?;\nconn.set_zoomed(SetPaneZoomed { containing_tab_id: entry.tab_id, pane_id, zoomed }).await?;","typeGuard":"fn tab_alive(res: &ListPanesResponse, tab_id: TabId) -> bool {\n    fn walk(n: &PaneNode, t: TabId) -> bool {\n        match n {\n            PaneNode::Leaf(e) => e.tab_id == t,\n            PaneNode::Split { left, right, .. } => walk(left, t) || walk(right, t),\n            PaneNode::Empty => false,\n        }\n    }\n    res.tabs.iter().any(|n| walk(n, tab_id))\n}","tryCatchPattern":"match conn.set_zoomed(SetPaneZoomed { containing_tab_id, pane_id, zoomed }).await {\n    Ok(r) => r,\n    Err(e) if e.to_string().contains(\"no such tab\") => {\n        // client cache desync: re-list, re-pair pane/tab ids, retry once\n        let fresh = conn.list_panes().await?;\n        let entry = find_entry(&fresh, pane_id).context(\"pane also gone\")?;\n        conn.set_zoomed(SetPaneZoomed { containing_tab_id: entry.tab_id, pane_id, zoomed }).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always pair pane_id and containing_tab_id from the same ListPanes snapshot; never mix snapshots","Invalidate the cached pane tree on TabClosed/WindowClosed notifications before issuing zoom/resize RPCs","Treat containing_tab_id as an opaque server-assigned value: never reuse it across a kill-server/restart"],"tags":["wezterm","mux","rpc","zoom","tab","cache-desync","stale-id"],"backgroundTag":"stale-tab-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"}