deepset-ai/haystack · error · RuntimeError

Pipeline has not finished; iterate the handle first.

Error message

Pipeline has not finished; iterate the handle first.

What it means

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.

Source

Thrown at haystack/core/pipeline/pipeline.py:98

                if item is self._END_OF_STREAM:
                    await self._task  # called to make exceptions surface
                    return
                yield cast(StreamingChunk, item)  # at this point, item is guaranteed to be a StreamingChunk

        finally:
            if self._cancel_on_abandon:
                await self.aclose()

    @property
    def result(self) -> dict[str, Any]:
        """
        Final pipeline output dict, available only after a successful, complete run.

        Raises a `RuntimeError` if the pipeline has not finished or was cancelled. If the pipeline failed, re-raises the
        original exception.
        """
        if not self._task.done():
            raise RuntimeError("Pipeline has not finished; iterate the handle first.")
        if self._task.cancelled():
            raise RuntimeError("Pipeline was cancelled; no result available.")
        exc = self._task.exception()
        if exc is not None:
            raise exc
        return self._task.result()

    async def aclose(self) -> None:
        """
        Cancel the underlying pipeline task.

        Bounded by `_CLEANUP_TIMEOUT_SECONDS` so that components cannot block cleanup indefinitely.
        """
        if not self._task.done():
            self._task.cancel()
            with contextlib.suppress(BaseException):
                await asyncio.wait_for(self._task, timeout=self._CLEANUP_TIMEOUT_SECONDS)

View on GitHub (pinned to e318778c9b)

Solutions

  1. Fully iterate the run handle (for _ in pipeline.run(...)) or await completion before calling result()
  2. Check task state with the handle before accessing result()
  3. Restructure code to await the coroutine if using async APIs

Example fix

// before
handle = pipeline.run_component_async(...)
print(handle.result())  # not done yet
// after
async def main():
    handle = pipeline.run_component_async(...)
    async for _ in handle:
        pass
    print(handle.result())
Defensive patterns

Strategy: try-catch

Type guard

def has_result(pipeline) -> bool:
    task = getattr(pipeline, "_task", None)
    return task is not None and task.done() and not task.cancelled()

Try / catch

try:
    result = handle.result()
except RuntimeError as e:
    if "has not finished" in str(e):
        # drain/await the handle first
        ...
    raise

Prevention

When it happens

Trigger: Accessing pipeline.result() right after creating/starting the run handle, before fully iterating it or awaiting completion.

Common situations: Mixing sync iteration with result() access, forgetting to await the run, or reading result() in a callback before the task finishes.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/a52c8e3bf5019ce2. Report an issue: GitHub.