From 20163f1bc43b834ba8c2006f20ff1f485ae4b2de Mon Sep 17 00:00:00 2001 From: tidely <43219534+tidely@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:03:31 +0200 Subject: [PATCH] agent: Fix panic caused by polling ended stream (#46839) Closes #ISSUE Reproduction steps: - Zed main branch - Use Ollama (likely provider agnostic) in the agent panel with the following prompt: "Explore the codebase with subagents" - Panic ``` thread 'main' (36268) panicked at C:\Users\username\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\futures-util-0.3.31\src\stream\unfold.rs:108:21: Unfold must not be polled after it returned `Poll::Ready(None)` ``` Following the stack trace we get to `Thread::run_turn_internal`. I believe the panic happens in the following code which was introduced in #46802 ```rust // Collect all immediately available events to process as a batch let mut batch = vec![first_event]; while let Some(event) = events.next().now_or_never().flatten() { batch.push(event); } ``` Both `Option`s get flattened, however the inner `Option` represents the end of the stream, after which polling the stream using `.next()` will result in a panic. We could fix the logic in this particular spot, but I believe the simpler solution is to `.fuse()` the stream, which stops the stream from panic'ing even after it has ended. This also prevents misuse in the future. The panic was introduces on main and did not land on a release yet, so no release notes. Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/agent/src/thread.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/agent/src/thread.rs b/crates/agent/src/thread.rs index 074e080a608eaa33ccb69d8eee7adbf50a46b90f..fb0f7dce19ee331f00a92123135632c4f9647206 100644 --- a/crates/agent/src/thread.rs +++ b/crates/agent/src/thread.rs @@ -1533,8 +1533,8 @@ impl Thread { log::debug!("Calling model.stream_completion, attempt {}", attempt); let (mut events, mut error) = match model.stream_completion(request, cx).await { - Ok(events) => (events, None), - Err(err) => (stream::empty().boxed(), Some(err)), + Ok(events) => (events.fuse(), None), + Err(err) => (stream::empty().boxed().fuse(), Some(err)), }; let mut tool_results = FuturesUnordered::new(); let mut cancelled = false;