From 5964a9f0d4dde007235a8349f9ebae082be033e1 Mon Sep 17 00:00:00 2001 From: Johannes du Plessis Date: Tue, 30 Jun 2026 17:59:56 -0700 Subject: [PATCH] chore: opt-in tracemalloc to attribute unclosed aiohttp sessions (#1657) Prod logs show bursts of 'Unclosed client session' (aiohttp), leaking fds + memory, but the warning omits the allocation site. When DEBUG_TRACEMALLOC is set, start tracemalloc at webapp import so aiohttp appends an 'Object allocated at' traceback naming the exact source. Inert when the env var is unset. Co-authored-by: open-swe[bot] (cherry picked from commit 320bb39ab1f5cd7a2acdac52c7b375854334176c) --- agent/webapp.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/agent/webapp.py b/agent/webapp.py index fdb11950..2538c17a 100644 --- a/agent/webapp.py +++ b/agent/webapp.py @@ -125,6 +125,26 @@ from .utils.slack_feedback import ( logger = logging.getLogger(__name__) +# Opt-in leak diagnostics. Bursts of aiohttp "Unclosed client session" warnings +# (from a third-party SDK) leak fds + memory in prod, but the warning omits the +# allocation site. With tracemalloc running, aiohttp appends an "Object allocated +# at" traceback to each warning, naming the exact source. Inert unless the env +# var is set, so this is safe to ship and flip on for one diagnostic run. +if os.environ.get("DEBUG_TRACEMALLOC"): + import tracemalloc + + try: + _tracemalloc_frames = int(os.environ.get("DEBUG_TRACEMALLOC_FRAMES") or "25") + except ValueError: + _tracemalloc_frames = 25 + tracemalloc.start(_tracemalloc_frames) + logger.warning( + "DEBUG_TRACEMALLOC enabled: tracemalloc started (%d frames) to attribute " + "unclosed-session warnings", + _tracemalloc_frames, + ) + + @asynccontextmanager async def lifespan(_app: FastAPI) -> AsyncIterator[None]: from .utils.model import validate_local_dev_llm_config