From 62021310289dd3f0f7fca4b77d5809e7f75c4a7d Mon Sep 17 00:00:00 2001 From: Ahmed Allam <49919286+0xallam@users.noreply.github.com> Date: Tue, 9 Jun 2026 01:46:23 -0700 Subject: [PATCH] Swallow sandbox container races in the stream consumer (#552) --- strix/core/agents.py | 4 ++++ strix/core/execution.py | 16 +++++++++++++--- strix/interface/tui/app.py | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/strix/core/agents.py b/strix/core/agents.py index 6aa5801..298aa2b 100644 --- a/strix/core/agents.py +++ b/strix/core/agents.py @@ -42,10 +42,14 @@ class AgentCoordinator: self.runtimes: dict[str, AgentRuntime] = {} self._lock = asyncio.Lock() self._snapshot_path: Path | None = None + self.is_shutting_down = False def set_snapshot_path(self, path: Path) -> None: self._snapshot_path = path + def mark_shutting_down(self) -> None: + self.is_shutting_down = True + async def register( self, agent_id: str, diff --git a/strix/core/execution.py b/strix/core/execution.py index 1017f70..1b561e9 100644 --- a/strix/core/execution.py +++ b/strix/core/execution.py @@ -11,6 +11,8 @@ from typing import TYPE_CHECKING, Any, cast from agents import RunConfig, Runner from agents.exceptions import AgentsException, MaxTurnsExceeded, UserError +from agents.sandbox.errors import ExecTransportError +from docker import errors as docker_errors # type: ignore[import-untyped, unused-ignore] from openai import APIError from strix.core.inputs import child_initial_input @@ -320,7 +322,7 @@ async def _run_noninteractive_until_lifecycle( ) -async def _run_cycle( # noqa: PLR0912 +async def _run_cycle( # noqa: PLR0912, PLR0915 agent: Any, coordinator: AgentCoordinator, agent_id: str, @@ -356,6 +358,8 @@ async def _run_cycle( # noqa: PLR0912 event_sink(agent_id, event) except Exception: logger.exception("stream event sink failed for %s", agent_id) + if stream.run_loop_exception is not None: + raise stream.run_loop_exception except RuntimeError as stream_exc: if "after shutdown" not in str(stream_exc): raise @@ -363,8 +367,14 @@ async def _run_cycle( # noqa: PLR0912 "Ignoring LiteLLM end-of-stream shutdown race for %s", agent_id, ) - if stream.run_loop_exception is not None: - raise stream.run_loop_exception + except (ExecTransportError, docker_errors.NotFound): + if not coordinator.is_shutting_down: + raise + logger.warning( + "Ignoring sandbox container error during teardown for %s", + agent_id, + exc_info=True, + ) finally: await coordinator.detach_stream(agent_id, stream) except Exception as exc: diff --git a/strix/interface/tui/app.py b/strix/interface/tui/app.py index 2b8f89b..e8fac0e 100644 --- a/strix/interface/tui/app.py +++ b/strix/interface/tui/app.py @@ -1715,6 +1715,7 @@ class StrixTUIApp(App): # type: ignore[misc] self.exit() def _fire_sandbox_cleanup(self) -> None: + self.coordinator.mark_shutting_down() loop = self._scan_loop if loop is None or loop.is_closed(): return