deepset-ai/haystack · error · TypeError

Items in the tools list must be Tool or Toolset instances.

Error message

Items in the tools list must be Tool or Toolset instances.

What it means

serialize_tools_or_toolset in haystack/tools/serde_utils.py serializes toolsets and tool lists for pipeline serialization (to_dict). When given a list, each item must be a Tool or Toolset and is serialized via its to_dict(); any other item raises TypeError. This guards pipeline save/loading against corrupted tool registries.

Source

Thrown at haystack/tools/serde_utils.py:33

def serialize_tools_or_toolset(tools: "ToolsType | None") -> dict[str, Any] | list[dict[str, Any]] | None:
    """
    Serialize tools or toolsets to dictionaries.

    :param tools: A Toolset, a list of Tools and/or Toolsets, or None
    :returns: Serialized representation preserving Tool/Toolset boundaries when provided
    """
    if tools is None:
        return None
    if isinstance(tools, Toolset):
        return tools.to_dict()
    if isinstance(tools, list):
        serialized: list[dict[str, Any]] = []
        for item in tools:
            if isinstance(item, (Toolset, Tool)):
                serialized.append(item.to_dict())
            else:
                raise TypeError("Items in the tools list must be Tool or Toolset instances.")
        return serialized
    raise TypeError("tools must be Toolset, list[Union[Tool, Toolset]], or None")


def deserialize_tools_or_toolset_inplace(data: dict[str, Any], key: str = "tools") -> None:
    """
    Deserialize a list of Tools and/or Toolsets, or a single Toolset in a dictionary inplace.

    :param data:
        The dictionary with the serialized data.
    :param key:
        The key in the dictionary where the list of Tools and/or Toolsets, or single Toolset is stored.
    """
    if key in data:
        serialized_tools = data[key]

        if serialized_tools is None:
            return

View on GitHub (pinned to e318778c9b)

Solutions

  1. Ensure every element of the tools list is an instantiated Tool or Toolset before serializing
  2. Replace raw dict representations with proper Tool.from_dict(...) / Toolset instances
  3. Filter out None or placeholder entries before to_dict()
  4. If you have a single Tool, confirm you're using the intended Toolset/Tool to_dict path

Example fix

// before
toolset.to_dict()  # tools contains raw dicts

// after
tools = [Tool.from_dict(d) if isinstance(d, dict) else d for d in raw_tools]
ts = Toolset(tools=tools)
ts.to_dict()
Defensive patterns

Strategy: validation

Validate before calling

from haystack.tools import Tool, Toolset

def validate_serializable_tools(tools) -> None:
    if tools is None or isinstance(tools, Toolset):
        return
    for i, item in enumerate(tools):
        if not isinstance(item, (Tool, Toolset)):
            raise TypeError(f"tools[{i}] is {type(item).__name__}; must be Tool or Toolset before to_dict()")

Type guard

def is_serializable_tools_list(tools) -> bool:
    from haystack.tools import Tool, Toolset
    return tools is None or isinstance(tools, Toolset) or (
        isinstance(tools, list) and all(isinstance(i, (Tool, Toolset)) for i in tools)
    )

Try / catch

try:
    data = toolset.to_dict()
except TypeError as e:
    logger.error("Tool registry corrupted for serialization: %s", e)
    raise

Prevention

When it happens

Trigger: Calling to_dict() on a Toolset/serializable component whose tools list contains non-Tool/non-Toolset items (dicts, raw callables, None, partially constructed objects).

Common situations: Manually building a tools list and inserting raw dicts or un-instantiated factories; deserialization bugs that left placeholders in the list; passing a single Tool (not in a list) with the list-handling path expected.

Related errors


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