deepset-ai/haystack · error · ComponentError

Output type specifications of 'run' and 'run_async' methods

Error message

Output type specifications of 'run' and 'run_async' methods must be the same

What it means

When a component defines both run and run_async, Haystack compares the cached @component.output_types specs of the two methods at instantiation time. They must be identical because the component's output sockets are shared by both entry points. Mismatched or missing decorations on one method trigger this ComponentError.

Source

Thrown at haystack/core/component/component.py:226

    def _parse_and_set_output_sockets(instance: Any) -> None:
        has_async_run = hasattr(instance, "run_async")

        # If `component.set_output_types()` was called in the component constructor,
        # `__haystack_output__` is already populated, no need to do anything.
        if not hasattr(instance, "__haystack_output__"):
            # If that's not the case, we need to populate `__haystack_output__`
            #
            # If either of the run methods were decorated, they'll have a field assigned that
            # stores the output specification. If both run methods were decorated, we ensure that
            # outputs are the same. We deepcopy the content of the cache to transfer ownership from
            # the class method to the actual instance, so that different instances of the same class
            # won't share this data.

            run_output_types = getattr(instance.run, "_output_types_cache", {})
            async_run_output_types = getattr(instance.run_async, "_output_types_cache", {}) if has_async_run else {}

            if has_async_run and run_output_types != async_run_output_types:
                raise ComponentError("Output type specifications of 'run' and 'run_async' methods must be the same")
            output_types_cache = run_output_types

            instance.__haystack_output__ = Sockets(instance, deepcopy(output_types_cache), OutputSocket)

    @staticmethod
    def _parse_and_set_input_sockets(component_cls: type, instance: Any) -> None:
        def inner(method: Callable[..., Any], sockets: Sockets) -> inspect.Signature:
            from inspect import Parameter

            run_signature = inspect.signature(method)
            # Resolves the annotations of components using postponed evaluation of annotations, where they are stored
            # as strings.
            param_types = _resolve_parameter_types(method)

            for param_name, param_info in run_signature.parameters.items():
                if param_name == "self" or param_info.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
                    continue

View on GitHub (pinned to e318778c9b)

Solutions

  1. Apply the identical @component.output_types(...) decorator to both run and run_async.
  2. If the outputs differ semantically, split into two components rather than sharing run/run_async.
  3. Check for typos in keys/types: the comparison is dict equality, so 'reply' vs 'response' or int vs str mismatches all fail.
  4. Alternatively, call component.set_output_types(...) in the constructor with the same spec, which bypasses the per-method caches.

Example fix

// before
@component.output_types(reply=str)
def run(self, q: str): ...
@component.output_types(answer=str)
async def run_async(self, q: str): ...
// after
@component.output_types(reply=str)
def run(self, q: str): ...
@component.output_types(reply=str)
async def run_async(self, q: str): ...
Defensive patterns

Strategy: validation

Validate before calling

from dataclasses import replace
run_spec = getattr(MyComponent.run, "_output_types_cache", {})
async_spec = getattr(MyComponent.run_async, "_output_types_cache", {})
assert run_spec == async_spec, f"output_types mismatch: {run_spec} vs {async_spec}"

Try / catch

try:
    comp = MyComponent()
except ComponentError as e:
    if "Output type specifications" in str(e):
        raise RuntimeError("Decorate run and run_async with identical @component.output_types") from e

Prevention

When it happens

Trigger: Defining run with @component.output_types(...) but run_async with a different (or no) output_types decoration; run_async decorated with different keys/types (e.g. run returns {'a': int} but run_async returns {'a': str}); one method's cache is empty because the decorator wasn't applied at all.

Common situations: Adding run_async to an existing component later and forgetting to mirror the decorators; copy-paste editing one method's output types without the other; refactoring renaming an output key in only one method.

Related errors


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