deepset-ai/haystack · error · TypeError

Invalid item type: {type(item)}. Must be Tool or str.

Error message

Invalid item type: {type(item)}. Must be Tool or str.

What it means

SearchableToolset.__contains__ (the 'in' operator) only accepts a Tool instance or a str tool name. Passing any other type (dict, Toolset, int, None) raises TypeError, since membership cannot be meaningfully checked.

Source

Thrown at haystack/tools/searchable_toolset.py:334

        if self._passthrough:
            yield from (tool for tool in self._catalog if self._is_selected(tool.name))
        else:
            if self._bootstrap_tool is not None:
                yield self._bootstrap_tool
            yield from (tool for tool in self._discovered_tools.values() if self._is_selected(tool.name))

    def __contains__(self, item: str | Tool) -> bool:
        """
        Check if a tool is available by Tool instance or tool name string.

        :param item: Tool instance or tool name string.
        :returns: True if the tool is available, False otherwise.
        """
        if isinstance(item, str):
            return any(tool.name == item for tool in self)
        if isinstance(item, Tool):
            return any(tool == item for tool in self)
        raise TypeError(f"Invalid item type: {type(item)}. Must be Tool or str.")

    def to_dict(self) -> dict[str, Any]:
        """
        Serialize the toolset to a dictionary.

        :returns: Dictionary representation of the toolset.
        """
        data: dict[str, Any] = {
            "catalog": serialize_tools_or_toolset(self._raw_catalog),
            "top_k": self._top_k,
            "search_threshold": self._search_threshold,
            "search_tool_name": self._search_tool_name,
            "search_tool_description": self._search_tool_description,
            "search_tool_parameters_description": self._search_tool_parameters_description,
        }

        return {"type": generate_qualified_class_name(type(self)), "data": data}

View on GitHub (pinned to e318778c9b)

Solutions

  1. Check with a str name: 'tool_name' in toolset
  2. Or check with the Tool instance itself: my_tool in toolset
  3. If you have a dict, extract the name: item['name'] in toolset

Example fix

// before
if {"name": "search"} in toolset: ...

// after
if "search" in toolset: ...
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_contains(toolset, item) -> bool:
    from haystack.tools import Tool
    if isinstance(item, (Tool, str)):
        return item in toolset
    if isinstance(item, dict) and isinstance(item.get("name"), str):
        return item["name"] in toolset
    raise TypeError(f"Cannot check membership for {type(item)}")

Type guard

def is_membership_key(item) -> bool:
    from haystack.tools import Tool
    return isinstance(item, (Tool, str))

Try / catch

try:
    present = item in toolset
except TypeError:
    name = getattr(item, "name", None) or (item.get("name") if isinstance(item, dict) else None)
    present = isinstance(name, str) and name in toolset

Prevention

When it happens

Trigger: x in searchable_toolset where x is a Toolset, dict, or other non-Tool/str object.

Common situations: Checking membership of a toolset inside another toolset; passing tool names wrapped in dicts like {'name': 'tool'}; sloppy checks before adding a tool.

Understand the failure class

Background: "Wrong argument type", "must be a string", "expected Array or Prism::Scope": TypeError and ArgumentError when a library receives a value of the wrong type — this error's family across 28 libraries.

Related errors


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