From 46b986c5c6dbc8e66ddcf1f7108fc19612a06b6d Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Fri, 13 Feb 2026 10:32:40 -0500 Subject: [PATCH] Fix 'no thread found' error when restoring agent thread on workspace open (#49045) When the multi-workspace thread restoration feature serializes the active thread's session ID, it includes empty threads that were never persisted to the database (because `NativeAgent::save_thread` skips empty threads). On restart, `AgentPanel::load()` tries to restore this thread by calling `load_agent_thread()`, which queries the SQLite DB. Since the empty thread was never saved, the query returns `None`, which becomes the error: "no thread found with ID: SessionId(...)". This shows as "Error Loading Zed Agent" / "Failed to Launch" in the UI. **Fix:** Before attempting to restore a serialized thread on panel load, query the DB to verify the thread actually exists. If it doesn't, log an error and skip restoration (the panel falls back to creating a new thread as normal). Closes AI-19 Release Notes: - Fixed an "Error Loading Zed Agent" error that could appear when opening a workspace where the previous agent thread was empty. --- crates/agent_ui/src/agent_panel.rs | 54 +++++++++++++++++++----------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 33b5acb9f376bf21744646075d172c32872c9346..4fb5acc10b32f7bd9aa324deb8a981aedeff6dcb 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -578,23 +578,41 @@ impl AgentPanel { }); } - if let Some(thread_info) = serialized_panel.and_then(|p| p.last_active_thread) { - let agent_type = thread_info.agent_type.clone(); - let session_info = AgentSessionInfo { - session_id: acp::SessionId::new(thread_info.session_id), - cwd: thread_info.cwd, - title: thread_info.title.map(SharedString::from), - updated_at: None, - meta: None, - }; - panel.update(cx, |panel, cx| { - panel.selected_agent = agent_type; - panel.load_agent_thread(session_info, window, cx); - }); - } panel })?; + if let Some(thread_info) = serialized_panel.and_then(|p| p.last_active_thread) { + let session_id = acp::SessionId::new(thread_info.session_id.clone()); + let load_task = panel.update(cx, |panel, cx| { + let thread_store = panel.thread_store.clone(); + thread_store.update(cx, |store, cx| store.load_thread(session_id, cx)) + }); + let thread_exists = load_task + .await + .map(|thread: Option| thread.is_some()) + .unwrap_or(false); + + if thread_exists { + panel.update_in(cx, |panel, window, cx| { + panel.selected_agent = thread_info.agent_type.clone(); + let session_info = AgentSessionInfo { + session_id: acp::SessionId::new(thread_info.session_id), + cwd: thread_info.cwd, + title: thread_info.title.map(SharedString::from), + updated_at: None, + meta: None, + }; + panel.load_agent_thread(session_info, window, cx); + })?; + } else { + log::error!( + "could not restore last active thread: \ + no thread found in database with ID {:?}", + thread_info.session_id + ); + } + } + Ok(panel) }) } @@ -3497,7 +3515,9 @@ mod tests { .expect("panel B load should succeed"); cx.run_until_parked(); - // Workspace A should restore its thread, width, and agent type + // Workspace A should restore width and agent type, but the thread + // should NOT be restored because the stub agent never persisted it + // to the database (the load-side validation skips missing threads). loaded_a.read_with(cx, |panel, _cx| { assert_eq!( panel.width, @@ -3508,10 +3528,6 @@ mod tests { panel.selected_agent, agent_type_a, "workspace A agent type should be restored" ); - assert!( - panel.active_thread_view().is_some(), - "workspace A should have its active thread restored" - ); }); // Workspace B should restore its own width and agent type, with no thread