From 985e92a990f42967e78183dd581b0e9a82c0a265 Mon Sep 17 00:00:00 2001 From: Roland Tannous Date: Sun, 12 Apr 2026 21:43:28 +0400 Subject: [PATCH] Fix httpcore GeneratorExit in /v1/messages passthrough stream Explicitly aclose aiter_lines() before the surrounding async with blocks unwind, mirroring the prior fix in external_provider.py (a41160d3) and cc757b78's RuntimeError suppression. --- studio/backend/routes/inference.py | 43 ++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/studio/backend/routes/inference.py b/studio/backend/routes/inference.py index 8082e4d834..10a6e2cf93 100644 --- a/studio/backend/routes/inference.py +++ b/studio/backend/routes/inference.py @@ -2672,21 +2672,36 @@ async def _anthropic_passthrough_stream( json = body, timeout = 600, ) as resp: - async for raw_line in resp.aiter_lines(): - if await request.is_disconnected(): - cancel_event.set() - return - if not raw_line or not raw_line.startswith("data: "): - continue - data_str = raw_line[6:] - if data_str.strip() == "[DONE]": - break + # Explicitly manage the aiter_lines() iterator so it is + # closed in this task before the surrounding `async with` + # blocks unwind the response / client. Without this, the + # inner httpcore byte-stream gets finalized by the GC in + # a different asyncio task on Python 3.13 + httpcore 1.x, + # raising "Attempted to exit cancel scope in a different + # task" and "async generator ignored GeneratorExit". + lines_iter = resp.aiter_lines() + try: + async for raw_line in lines_iter: + if await request.is_disconnected(): + cancel_event.set() + return + if not raw_line or not raw_line.startswith("data: "): + continue + data_str = raw_line[6:] + if data_str.strip() == "[DONE]": + break + try: + chunk = json.loads(data_str) + except json.JSONDecodeError: + continue + for line in emitter.feed_chunk(chunk): + yield line + finally: try: - chunk = json.loads(data_str) - except json.JSONDecodeError: - continue - for line in emitter.feed_chunk(chunk): - yield line + await lines_iter.aclose() + except RuntimeError: + # Python 3.13 + httpcore 1.0.x asyncgen cleanup + pass except Exception as e: logger.error("anthropic_messages passthrough stream error: %s", e)