sxyazi/yazi · error · anyhow::Error
Tab with id {id} not found
Error message
Tab with id {id} not found What it means
Ctx::new resolves the target tab for every action: if the Action carries a "tab" id it is looked up with core.mgr.tabs.idx(id), otherwise the current cursor tab is used. An id that matches no open tab makes construction fail with "Tab with id {id} not found" before the actor runs. Tab ids are only valid while that tab is alive; closing a tab invalidates previously captured ids.
Source
Thrown at yazi-actor/src/context.rs:32
pub source: Source,
#[cfg(debug_assertions)]
pub backtrace: Vec<&'static str>,
}
impl Deref for Ctx<'_> {
type Target = Core;
fn deref(&self) -> &Self::Target { self.core }
}
impl DerefMut for Ctx<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { self.core }
}
impl<'a> Ctx<'a> {
pub fn new(action: &Action, core: &'a mut Core, term: &'a mut Option<Raterm>) -> Result<Self> {
let tab = if let Ok(id) = action.get::<Id>("tab") {
core.mgr.tabs.idx(id).ok_or_else(|| anyhow!("Tab with id {id} not found"))?
} else {
core.mgr.tabs.cursor
};
Ok(Self {
core,
term,
tab,
level: 0,
source: action.source,
#[cfg(debug_assertions)]
backtrace: vec![],
})
}
pub fn with<F, T>(&mut self, tab: usize, f: F) -> T
where
F: FnOnce(&mut Self) -> T,View on GitHub (pinned to 94abcfa92f)
Solutions
- Drop the "tab" argument from the action so Ctx::new falls back to the current tab (core.mgr.tabs.cursor)
- Re-read the live tab id from the latest Tab event payload instead of caching it across events
- Check validity first: core.mgr.tabs.idx(id) (and tabs.len()) before constructing the action
- If the tab was legitimately closed, treat the error as benign and ignore/skip the action
Example fix
// before: stale cached id
emit!(Call(relay!(mgr:hover).with("tab", cached_id)));
// after: let Ctx pick the current tab
emit!(Call(relay!(mgr:hover))); Defensive patterns
Strategy: validation
Validate before calling
// Before emitting an action with an explicit tab id:
if core.mgr.tabs.idx(id).is_none() {
// stale id: fall back to the current tab
emit!(Call(relay!(mgr:hover)));
return Ok(());
}
emit!(Call(relay!(mgr:hover).with("tab", id))); Type guard
fn tab_exists(mgr: &Mgr, id: Id) -> bool { mgr.tabs.idx(id).is_some() } Try / catch
match Ctx::new(&action, core, term) {
Ok(cx) => { /* run actor */ }
Err(e) if e.to_string().starts_with("Tab with id") => { /* stale tab: drop or re-emit without `tab` */ }
Err(e) => return Err(e),
} Prevention
- Never cache tab ids across events; read them from the current Tab payload
- Omit the "tab" argument unless you truly must target a specific tab
- Assume tab ids are invalidated by close; they are not stable handles
When it happens
Trigger: Emitting or relaying an action that includes a "tab" argument (e.g. relay!(mgr:...).with("tab", id)) after that tab was closed, or passing an id that was never allocated by mgr.tabs. Also any deferred/queued action whose captured id goes stale before dispatch.
Common situations: Plugins caching a tab id from an earlier Tab event and emitting actions later; keymap or plugin snippets hardcoding a tab id; driving a yazi instance via ya-emitted actions while the user closes tabs in between.
Related errors
- Missing 'payload' in Payload
- Invalid 'opt' in ShowForm
- Invalid 'opt' in ShowForm
- trash item outside of trash folders
AI-assisted analysis of sxyazi/yazi@94abcfa92f (2026-08-16).
Data as JSON: /api/errors/9ee5bc740a6933be.
Report an issue: GitHub.