{"record":{"id":"a52c8e3bf5019ce2","repo":"deepset-ai/haystack","slug":"pipeline-has-not-finished-iterate-the-handle-firs","errorCode":null,"errorMessage":"Pipeline has not finished; iterate the handle first.","messagePattern":"Pipeline has not finished; iterate the handle first\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"haystack/core/pipeline/pipeline.py","lineNumber":98,"sourceCode":"                if item is self._END_OF_STREAM:\n                    await self._task  # called to make exceptions surface\n                    return\n                yield cast(StreamingChunk, item)  # at this point, item is guaranteed to be a StreamingChunk\n\n        finally:\n            if self._cancel_on_abandon:\n                await self.aclose()\n\n    @property\n    def result(self) -> dict[str, Any]:\n        \"\"\"\n        Final pipeline output dict, available only after a successful, complete run.\n\n        Raises a `RuntimeError` if the pipeline has not finished or was cancelled. If the pipeline failed, re-raises the\n        original exception.\n        \"\"\"\n        if not self._task.done():\n            raise RuntimeError(\"Pipeline has not finished; iterate the handle first.\")\n        if self._task.cancelled():\n            raise RuntimeError(\"Pipeline was cancelled; no result available.\")\n        exc = self._task.exception()\n        if exc is not None:\n            raise exc\n        return self._task.result()\n\n    async def aclose(self) -> None:\n        \"\"\"\n        Cancel the underlying pipeline task.\n\n        Bounded by `_CLEANUP_TIMEOUT_SECONDS` so that components cannot block cleanup indefinitely.\n        \"\"\"\n        if not self._task.done():\n            self._task.cancel()\n            with contextlib.suppress(BaseException):\n                await asyncio.wait_for(self._task, timeout=self._CLEANUP_TIMEOUT_SECONDS)\n","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/core/pipeline/pipeline.py#L80-L116","documentation":"Pipeline.result() returns the final output dict of an asynchronously/completely run pipeline. It raises RuntimeError if the underlying asyncio task has not completed yet, since no final output exists.","triggerScenarios":"Accessing pipeline.result() right after creating/starting the run handle, before fully iterating it or awaiting completion.","commonSituations":"Mixing sync iteration with result() access, forgetting to await the run, or reading result() in a callback before the task finishes.","solutions":["Fully iterate the run handle (for _ in pipeline.run(...)) or await completion before calling result()","Check task state with the handle before accessing result()","Restructure code to await the coroutine if using async APIs"],"exampleFix":"// before\nhandle = pipeline.run_component_async(...)\nprint(handle.result())  # not done yet\n// after\nasync def main():\n    handle = pipeline.run_component_async(...)\n    async for _ in handle:\n        pass\n    print(handle.result())","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"def has_result(pipeline) -> bool:\n    task = getattr(pipeline, \"_task\", None)\n    return task is not None and task.done() and not task.cancelled()","tryCatchPattern":"try:\n    result = handle.result()\nexcept RuntimeError as e:\n    if \"has not finished\" in str(e):\n        # drain/await the handle first\n        ...\n    raise","preventionTips":["Always fully iterate or await the run handle before result()","Check task.done() before accessing results","Centralize pipeline-run helpers so completion handling is consistent"],"tags":["python","asyncio","concurrency","pipeline"],"backgroundTag":"result-accessed-before-complete","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}