{"record":{"id":"510c2279e75d0054","repo":"deepset-ai/haystack","slug":"pipelineruntimeerror-from-invalid-output-component-510c22","errorCode":null,"errorMessage":"PipelineRuntimeError.from_invalid_output(component_name, instance.__class__, outputs)","messagePattern":"PipelineRuntimeError\\.from_invalid_output\\(component_name, instance\\.__class__, outputs\\)","errorType":"exception","errorClass":"PipelineRuntimeError","httpStatus":null,"severity":"error","filePath":"haystack/core/pipeline/pipeline.py","lineNumber":591,"sourceCode":"            component_name=component_name, instance=instance, inputs=component_inputs, parent_span=parent_span\n        ) as span:\n            # deepcopy inputs before passing to the tracer so that even if a tracer mutates them\n            # the component always receives the original unmodified values\n            component_inputs_copy = _deepcopy_with_exceptions(component_inputs)\n            span.set_content_tag(_COMPONENT_INPUT, component_inputs)\n            logger.info(\"Running component {component_name}\", component_name=component_name)\n\n            try:\n                # For sync-only components, _run_component_async dispatches to a thread via asyncio.to_thread,\n                # which copies the current contextvars context — preserving e.g. the active tracing span.\n                outputs = await _execute_component_async(instance, **component_inputs_copy)\n            except Exception as error:\n                raise PipelineRuntimeError.from_exception(component_name, instance.__class__, error) from error\n\n            component_visits[component_name] += 1\n\n            if not isinstance(outputs, Mapping):\n                raise PipelineRuntimeError.from_invalid_output(component_name, instance.__class__, outputs)\n\n            _validate_component_output_keys(component_name, component, outputs)\n\n            span.set_tag(_COMPONENT_VISITS, component_visits[component_name])\n            span.set_content_tag(_COMPONENT_OUTPUT, outputs)\n\n            return outputs\n\n    @staticmethod\n    async def _wait_for_tasks(\n        running_tasks: dict[asyncio.Task, str], scheduled_components: set[str], *, return_when: str\n    ) -> AsyncIterator[dict[str, Any]]:\n        \"\"\"\n        Waits for running tasks to finish and yields their partial outputs.\n\n        :param running_tasks: Mapping of in-flight tasks to the name of the component they run. Finished tasks are\n            removed in place.\n        :param scheduled_components: Set of component names that are scheduled but not yet finished. Finished","sourceCodeStart":573,"sourceCodeEnd":609,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/core/pipeline/pipeline.py#L573-L609","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nclass Sum:\n    @component.output_types(total=int)\n    def run(self, a: int, b: int):\n        return a + b  # not a Mapping\n# after\nclass Sum:\n    @component.output_types(total=int)\n    def run(self, a: int, b: int):\n        return {\"total\": a + b}\n","handlingStrategy":"type-guard","validationCode":"result = my_component.run(**inputs)\nif not isinstance(result, Mapping):\n    raise TypeError('component must return a Mapping')","typeGuard":"from collections.abc import Mapping\ndef returns_mapping(result) -> bool:\n    return isinstance(result, Mapping)","tryCatchPattern":"try:\n    result = pipe.run(data)\nexcept PipelineRuntimeError as e:\n    if 'did not return a Mapping' in str(e):\n        print('fix the component return type named in the message')","preventionTips":["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."],"tags":["pipeline","component","type-error","return-type"],"backgroundTag":"component-invalid-output-type","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}