deepset-ai/haystack · error · PipelineRuntimeError
PipelineRuntimeError.from_invalid_output(component_name, ins
Error message
PipelineRuntimeError.from_invalid_output(component_name, instance.__class__, outputs)
What it means
PipelineRuntimeError.from_invalid_output is raised in _run_component_async (pipeline.py:591) when a component's run returns a value that is not a Mapping (dict-like). Haystack components must return a dictionary of output sockets so the pipeline can distribute outputs to downstream connections. A non-mapping return value makes output routing impossible.
Source
Thrown at haystack/core/pipeline/pipeline.py:591
component_name=component_name, instance=instance, inputs=component_inputs, parent_span=parent_span
) as span:
# deepcopy inputs before passing to the tracer so that even if a tracer mutates them
# the component always receives the original unmodified values
component_inputs_copy = _deepcopy_with_exceptions(component_inputs)
span.set_content_tag(_COMPONENT_INPUT, component_inputs)
logger.info("Running component {component_name}", component_name=component_name)
try:
# For sync-only components, _run_component_async dispatches to a thread via asyncio.to_thread,
# which copies the current contextvars context — preserving e.g. the active tracing span.
outputs = await _execute_component_async(instance, **component_inputs_copy)
except Exception as error:
raise PipelineRuntimeError.from_exception(component_name, instance.__class__, error) from error
component_visits[component_name] += 1
if not isinstance(outputs, Mapping):
raise PipelineRuntimeError.from_invalid_output(component_name, instance.__class__, outputs)
_validate_component_output_keys(component_name, component, outputs)
span.set_tag(_COMPONENT_VISITS, component_visits[component_name])
span.set_content_tag(_COMPONENT_OUTPUT, outputs)
return outputs
@staticmethod
async def _wait_for_tasks(
running_tasks: dict[asyncio.Task, str], scheduled_components: set[str], *, return_when: str
) -> AsyncIterator[dict[str, Any]]:
"""
Waits for running tasks to finish and yields their partial outputs.
:param running_tasks: Mapping of in-flight tasks to the name of the component they run. Finished tasks are
removed in place.
:param scheduled_components: Set of component names that are scheduled but not yet finished. FinishedView on GitHub (pinned to e318778c9b)
Solutions
- Change the component's run() to return a dict keyed by declared output socket names.
- If the result can be absent, return an empty dict or a dict with an explicit key rather than None.
- Add a unit test asserting isinstance(result, Mapping) for the component.
Example fix
# before
class Sum:
@component.output_types(total=int)
def run(self, a: int, b: int):
return a + b # not a Mapping
# after
class Sum:
@component.output_types(total=int)
def run(self, a: int, b: int):
return {"total": a + b}
Defensive patterns
Strategy: type-guard
Validate before calling
result = my_component.run(**inputs)
if not isinstance(result, Mapping):
raise TypeError('component must return a Mapping') Type guard
from collections.abc import Mapping
def returns_mapping(result) -> bool:
return isinstance(result, Mapping) Try / catch
try:
result = pipe.run(data)
except PipelineRuntimeError as e:
if 'did not return a Mapping' in str(e):
print('fix the component return type named in the message') Prevention
- Always use @component.output_types and return a dict matching declared sockets.
- Unit-test each custom component's run() return type before wiring into pipelines.
- Return {} rather than None for 'no output' cases.
- Enable type checkers on component run methods to catch wrong annotations early.
When it happens
Trigger: A custom component's run() returns a list, tuple, string, DataFrame, or None instead of a dict; a decorator-refactored component whose return type changed.
Common situations: Hand-written components returning bare values or structured objects; components migrated from older Haystack versions with different return conventions; code that returns Optional[dict] and hits the None path.
Related errors
- {type(self.chat_generator).__name__} does not accept tools p
- Hook registered for hook point '{hook_point}' is callable bu
- The {self.__class__.__name__} expects a list containing only
- Unsupported source type {type(source)}
- meta must be either None, a dictionary or a list of dictiona
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/510c2279e75d0054.
Report an issue: GitHub.