{"record":{"id":"9ef6462bf56cddb5","repo":"zylon-ai/private-gpt","slug":"async-iterator-conversion-failed-e-s","errorCode":null,"errorMessage":"Async iterator conversion failed: {e!s}","messagePattern":"Async iterator conversion failed: (.+?)","errorType":"exception","errorClass":"AsyncIteratorError","httpStatus":null,"severity":"error","filePath":"private_gpt/utils/async_utils.py","lineNumber":94,"sourceCode":"            for item in chunk:\n                try:\n                    if transform_fn:\n                        # Run transform in executor if it's CPU-intensive\n                        result = await loop.run_in_executor(\n                            internal_executor, transform_fn, item\n                        )\n                        yield result\n                    else:\n                        yield item\n                except Exception as e:\n                    raise AsyncIteratorError(\n                        f\"Item transformation failed: {e!s}\"\n                    ) from e\n\n    except asyncio.CancelledError:\n        raise\n    except Exception as e:\n        raise AsyncIteratorError(f\"Async iterator conversion failed: {e!s}\") from e\n    finally:\n        if not executor:\n            internal_executor.shutdown(wait=False)\n","sourceCodeStart":76,"sourceCodeEnd":98,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/utils/async_utils.py#L76-L98","documentation":"The outermost safety net of to_async_iterator: any exception escaping the conversion loop that is not caught by the inner handlers (iterator next, transform) and is not asyncio.CancelledError is re-raised as AsyncIteratorError('Async iterator conversion failed: ...') with the original exception chained. It usually indicates an infrastructure failure (executor shutdown, awaiting run_in_executor on a closed loop, chunk handling bug) rather than a data problem.","triggerScenarios":"The event loop being closed while the generator is still running; the supplied ThreadPoolExecutor being shut down externally mid-iteration; cancellation racing the executor (CancelledError is re-raised untouched, but adjacent errors surface here); internal errors like appending to a chunk after the executor raised KeyboardInterrupt-adjacent exceptions.","commonSituations":"Test suites that close the loop before consuming the async generator fully; FastAPI/uvicorn shutdown while a streaming response backed by to_async_iterator is in flight; sharing one executor across components that shut it down at different lifetimes.","solutions":["Read the chained cause (__cause__) - the real failure is the original exception, not this wrapper","Ensure the async generator is fully consumed or properly closed (aclose()) before shutting down the loop/app","Do not share a ThreadPoolExecutor across components with different lifetimes; pass executor=None to let the wrapper own and shut down its own executor","In web handlers, wrap consumption in try/except AsyncIteratorError and convert to a clean 500/stream-abort response"],"exampleFix":"# before\nait = to_async_iterator(iter(docs), transform_fn=fn, executor=shared_pool)\n... later: shared_pool.shutdown(wait=True)  # while ait still running\n\n# after\nait = to_async_iterator(iter(docs), transform_fn=fn)  # wrapper owns executor\ntry:\n    async for item in ait:\n        handle(item)\nexcept AsyncIteratorError as e:\n    logger.error(f\"stream failed: {e}\", exc_info=e.__cause__)","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try:\n    async for item in to_async_iterator(it, transform_fn=fn):\n        handle(item)\nexcept asyncio.CancelledError:\n    raise  # let cancellation propagate; wrapper re-raises it untouched\nexcept AsyncIteratorError as e:\n    logger.error(\"stream infrastructure failure: %s\", e.__cause__)\n    # convert to clean abort; do not retry blindly","preventionTips":["Fully consume or explicitly aclose() the async generator before app/loop shutdown","Do not share a ThreadPoolExecutor with components that may shut it down early; let the wrapper create its own","In streaming HTTP handlers, map AsyncIteratorError to a clean response termination","Always inspect e.__cause__ - the wrapper message hides the real exception class"],"tags":["async","lifecycle","error-wrapping","concurrency"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}