zed-industries/zed · error

Terminal with id `{}` not found

Error message

Terminal with id `{}` not found

What it means

Converting acp::ToolCallContent::Terminal into the local representation requires the terminal_id to be present in the terminals map passed to the conversion. When the referenced terminal is not tracked there, conversion fails with the missing id.

Source

Thrown at crates/acp_thread/src/acp_thread.rs:1844

                    &language_registry,
                    path_style,
                    cx,
                )),
            )),
            acp::ToolCallContent::Diff(diff) => Ok(Some(Self::Diff(cx.new(|cx| {
                Diff::finalized(
                    diff.path.to_string_lossy().into_owned(),
                    diff.old_text,
                    diff.new_text,
                    language_registry,
                    cx,
                )
            })))),
            acp::ToolCallContent::Terminal(acp::Terminal { terminal_id, .. }) => terminals
                .get(&terminal_id)
                .cloned()
                .map(|terminal| Some(Self::Terminal(terminal)))
                .ok_or_else(|| anyhow::anyhow!("Terminal with id `{}` not found", terminal_id)),
            _ => Ok(None),
        }
    }

    pub fn update_from_acp(
        &mut self,
        new: acp::ToolCallContent,
        language_registry: Arc<LanguageRegistry>,
        path_style: PathStyle,
        terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
        cx: &mut App,
    ) -> Result<bool> {
        // Update streaming text in place so the rendered markdown element is
        // reused across snapshots instead of being recreated (which flickers).
        if let (
            Self::ContentBlock(block),
            acp::ToolCallContent::Content(acp::Content { content, .. }),
        ) = (&mut *self, &new)

View on GitHub (pinned to bc538def45)

Solutions

  1. Populate the terminals map (keyed by acp::TerminalId) before forwarding or replaying tool-call content that references terminals.
  2. When an unknown id is seen, defer or drop that content block instead of failing the entire conversion.
  3. When replaying persisted threads, recreate terminals under their persisted ids or strip Terminal blocks first.

Example fix

// before
let content = ToolCallContent::from_acp(raw, registry, path_style, &terminals, cx)?; // fails on unknown id

// after
if let acp::ToolCallContent::Terminal(t) = &raw {
    if !terminals.contains_key(&t.terminal_id) {
        return Ok(None); // skip block instead of erroring
    }
}
let content = ToolCallContent::from_acp(raw, registry, path_style, &terminals, cx)?;
Defensive patterns

Strategy: validation

Validate before calling

if let acp::ToolCallContent::Terminal(acp::Terminal { terminal_id, .. }) = &content {
    if !terminals.contains_key(terminal_id) {
        // skip or defer this block; do not attempt conversion
        return Ok(None);
    }
}

Type guard

fn terminal_is_tracked(
    terminal_id: &acp::TerminalId,
    terminals: &HashMap<acp::TerminalId, Entity<Terminal>>,
) -> bool {
    terminals.contains_key(terminal_id)
}

Try / catch

match ToolCallContent::from_acp(raw, registry, path_style, &terminals, cx) {
    Ok(content) => { /* render */ }
    Err(e) if e.to_string().starts_with("Terminal with id") => {
        // unknown terminal: drop the block, keep the rest of the tool call
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A tool-call content event referencing a terminal id that was never registered in the HashMap — events arriving before the terminal is created, from another window, or after the terminal was removed; replaying persisted threads whose terminals were not restored.

Common situations: Deserializing recorded/replayed sessions without recreating terminals; ordering races between terminal creation and tool-call content events; multi-window setups.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/4b744b2e830d5396. Report an issue: GitHub.