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
- 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
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
- 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
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
- Pipeline was cancelled; no result available.
- MarkdownHeaderSplitter only works with text documents but co
- Error while unmarshalling serialized pipeline data. This is
- Component instance cannot be added to the pipeline more than
- A component named '{name}' already exists in this pipeline:
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/a52c8e3bf5019ce2.
Report an issue: GitHub.