deepset-ai/haystack · error · ComponentError

Parameters of 'run' and 'run_async' methods must be the same

Error message

Parameters of 'run' and 'run_async' methods must be the same.
Differences found:
{sig_diff}

What it means

ComponentMeta validates that a component's 'run' and 'run_async' methods declare exactly the same parameters (and matching input sockets). If they diverge, class instantiation raises ComponentError with a signature diff. Haystack requires both so sync and async pipeline execution behave identically.

Source

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

        if not hasattr(instance, "__haystack_input__"):
            instance.__haystack_input__ = Sockets(instance, {}, InputSocket)

        inner(getattr(component_cls, "run"), instance.__haystack_input__)  # noqa: B009

        # Ensure that the sockets are the same for the async method, if it exists.
        async_run = getattr(component_cls, "run_async", None)
        if async_run is not None:
            run_sockets = Sockets(instance, {}, InputSocket)
            async_run_sockets = Sockets(instance, {}, InputSocket)

            # Can't use the sockets from above as they might contain
            # values set with set_input_types().
            run_sig = inner(getattr(component_cls, "run"), run_sockets)  # noqa: B009
            async_run_sig = inner(async_run, async_run_sockets)

            if async_run_sockets != run_sockets or run_sig != async_run_sig:
                sig_diff = _compare_run_methods_signatures(run_sig, async_run_sig)
                raise ComponentError(
                    f"Parameters of 'run' and 'run_async' methods must be the same.\nDifferences found:\n{sig_diff}"
                )

    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        """
        This method is called when clients instantiate a Component and runs before __new__ and __init__.
        """
        # This will call __new__ then __init__, giving us back the Component instance
        pre_init_hook = _COMPONENT_PRE_INIT_HOOK.get()
        if pre_init_hook is None or pre_init_hook.in_progress:
            instance = super().__call__(*args, **kwargs)
        else:
            try:
                pre_init_hook.in_progress = True
                named_positional_args = ComponentMeta._positional_to_kwargs(cls, args)
                assert set(named_positional_args.keys()).intersection(kwargs.keys()) == set(), (
                    "positional and keyword arguments overlap"
                )

View on GitHub (pinned to e318778c9b)

Solutions

  1. Make run_async declare exactly the same parameters, annotations, and defaults as run
  2. Apply the same @component.input_types / set_input_types configuration to both methods
  3. Read the sig_diff in the message to see which parameter differs, then fix that parameter in the offending method
  4. If async support is not needed, delete run_async entirely (then only run is validated)

Example fix

# before
class Sum:
    def run(self, a: int, b: int = 0):
        return {"result": a + b}
    async def run_async(self, a: int):  # missing b, default differs
        return {"result": a}

# after
class Sum:
    def run(self, a: int, b: int = 0):
        return {"result": a + b}
    async def run_async(self, a: int, b: int = 0):
        return {"result": a + b}
Defensive patterns

Strategy: validation

Validate before calling

import inspect
def validate_run_signatures(cls):
    run, run_async = getattr(cls, "run", None), getattr(cls, "run_async", None)
    if run and run_async:
        rs, asig = inspect.signature(run), inspect.signature(run_async)
        if rs.parameters != asig.parameters:
            raise ValueError(f"run/run_async mismatch: {rs} vs {asig}")

Type guard

def has_matching_signatures(cls) -> bool:
    r, ra = getattr(cls, "run", None), getattr(cls, "run_async", None)
    return not (r and ra) or inspect.signature(r).parameters == inspect.signature(ra).parameters

Prevention

When it happens

Trigger: Defining a component class where run and run_async have different parameter names, different type annotations, different defaults, or different sets of parameters; only run is decorated with @component.input_types while run_async is not (or vice versa), producing mismatched sockets.

Common situations: Adding or renaming a parameter in run but forgetting run_async; decorating only one of the two methods with the input_types decorator; hand-writing run_async instead of using a helper that mirrors run.

Related errors


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