From c6dee5f49f9d4763cb498904d9bf0d0fd0c4586b Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Fri, 14 Aug 2026 00:21:27 +0000 Subject: [PATCH] Preserve thread subscriptions across revert reloads (#38463) ## Why The connection that requested `thread/revert` may close while the thread is being reloaded, but the replacement listener still needs to serve the thread's existing subscriptions. ## What changed Restart the listener task from the preserved thread state after a paginated thread reload instead of attaching it through the requesting connection. GitOrigin-RevId: 603ec3331c59bdde48faa2f4cb0fd1032ec8dbd8 --- .../src/request_processors/thread_processor.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/codex-rs/app-server/src/request_processors/thread_processor.rs b/codex-rs/app-server/src/request_processors/thread_processor.rs index 06f670b56e..4bb5e435f8 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -2064,17 +2064,12 @@ impl ThreadRequestProcessor { "rollout path missing after reloading thread {thread_id}" )) })?; - log_listener_attach_result( - self.ensure_conversation_listener( - thread_id, - request_id.connection_id, - /*raw_events_enabled*/ false, - ) - .await, - thread_id, - request_id.connection_id, - "thread", - ); + // Revert keeps the existing thread state and subscriptions across the internal reload. + // Start the replacement listener from that state instead of depending on the requesting + // connection still being open. + let thread_state = self.thread_state_manager.thread_state(thread_id).await; + self.ensure_listener_task_running(thread_id, Arc::clone(&codex_thread), thread_state) + .await?; let mut thread = self .load_thread_from_resume_source_or_send_internal( thread_id,