From 0237672fc2069f50cac7048cdc01a35fa8b3b611 Mon Sep 17 00:00:00 2001 From: Sarthak Singh Date: Mon, 3 Aug 2026 00:48:21 +0530 Subject: [PATCH] fix(4369): invalidate session on -32603 dead-child error in run_prompt_task MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a session's agent child process dies out-of-band, session/prompt returns JSON-RPC -32603 'Internal error'. The existing error arm treated all AgentError variants identically — 'pipe intact, don't invalidate' — so the session cache pointed at the dead session and the retry loop re-prompted the same dead session every attempt until the batch dead-lettered. Two channels were deaf for ~4 hours with 52 owner events dead-lettered in production. Fix: carve out -32603 as session-invalidating. The exception is keyed on the JSON-RPC error code itself, not a new AcpError variant, so the classification stays close to the wire format. False positives are cheap (one extra session/new); false negatives wedge the channel. Closes #4369 Signed-off-by: Sarthak Singh --- crates/buzz-acp/src/pool.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/buzz-acp/src/pool.rs b/crates/buzz-acp/src/pool.rs index 348bc138e4..2613694314 100644 --- a/crates/buzz-acp/src/pool.rs +++ b/crates/buzz-acp/src/pool.rs @@ -2278,7 +2278,16 @@ pub async fn run_prompt_task( // AgentError means the agent caught a problem before mutating // session state (e.g. bad LLM response). The session is healthy — // don't invalidate it. Other errors may have corrupted state. - if !matches!(e, AcpError::AgentError { .. }) { + // + // Exception (#4369): -32603 "Internal error" from `session/prompt` + // means the underlying agent child process died out-of-band. The + // session is dead, not healthy — invalidate so the retry misses + // the session cache and create_session_and_apply_model builds a + // fresh session on the next attempt. False positives are cheap + // (one extra session/new); false negatives wedge the channel. + let is_dead_session = + matches!(e, AcpError::AgentError { code: -32603, .. }); + if is_dead_session || !matches!(e, AcpError::AgentError { .. }) { agent.state.invalidate(&source); } let usage = agent.acp.take_turn_usage();