deepset-ai/haystack · error · TypeError

Invalid catalog type: {type(catalog)}. Expected Tool, Toolse

Error message

Invalid catalog type: {type(catalog)}. Expected Tool, Toolset, or list of Tools and/or Toolsets.

What it means

SearchableToolset.__init__ validates its 'catalog' argument: it must be a Toolset, or a list whose items are all Tool or Toolset instances. Anything else (a single bare Tool, a string, None, a list containing non-tool items) raises TypeError.

Source

Thrown at haystack/tools/searchable_toolset.py:104

        """
        Initialize the SearchableToolset.

        :param catalog: Source of tools - a list of Tools, list of Toolsets, or a single Toolset.
        :param top_k: Default number of results for search_tools.
        :param search_threshold: Minimum catalog size to activate search. If catalog has fewer tools, acts as
            passthrough (all tools visible). Default is 8.
        :param search_tool_name: Custom name for the bootstrap search tool. Default is "search_tools".
        :param search_tool_description: Custom description for the bootstrap search tool. If not provided, uses a
            default description.
        :param search_tool_parameters_description: Custom descriptions for the bootstrap search tool's parameters.
            Keys must be a subset of `{"tool_keywords", "k"}`.
            Example: `{"tool_keywords": "Keywords to find tools, e.g. 'email send'"}`
        """
        valid_catalog = isinstance(catalog, Toolset) or (
            isinstance(catalog, list) and all(isinstance(item, (Tool, Toolset)) for item in catalog)
        )
        if not valid_catalog:
            raise TypeError(
                f"Invalid catalog type: {type(catalog)}. Expected Tool, Toolset, or list of Tools and/or Toolsets."
            )

        if search_tool_parameters_description is not None:
            invalid_keys = set(search_tool_parameters_description.keys()) - self._VALID_SEARCH_TOOL_PARAMS
            if invalid_keys:
                raise ValueError(
                    f"Invalid search_tool_parameters_description keys: {invalid_keys}. "
                    f"Valid keys are: {self._VALID_SEARCH_TOOL_PARAMS}"
                )

        # Store raw catalog; flattening is deferred to warm_up() so that lazy toolsets
        # (e.g. MCPToolset with eager_connect=False) can connect first.
        self._raw_catalog: "ToolsType" = catalog
        self._catalog: list[Tool] = []

        self._top_k = top_k
        self._search_threshold = search_threshold

View on GitHub (pinned to e318778c9b)

Solutions

  1. Wrap a single Tool in a list: SearchableToolset(catalog=[my_tool])
  2. Ensure every list item is a Tool or Toolset instance — instantiate tools before passing them
  3. Pass a Toolset (e.g. Toolset(tools=[...])) instead of an ad-hoc list

Example fix

// before
SearchableToolset(catalog=my_tool)

// after
SearchableToolset(catalog=[my_tool])
Defensive patterns

Strategy: type-guard

Validate before calling

from haystack.tools import Tool, Toolset

def validate_catalog(catalog) -> bool:
    if isinstance(catalog, Toolset):
        return True
    return isinstance(catalog, list) and all(isinstance(i, (Tool, Toolset)) for i in catalog)

Type guard

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

Try / catch

try:
    ts = SearchableToolset(catalog=catalog)
except TypeError as e:
    if isinstance(catalog, Tool):
        catalog = [catalog]
    ts = SearchableToolset(catalog=catalog)

Prevention

When it happens

Trigger: SearchableToolset(catalog=single_tool) passing one Tool instead of [tool]; passing a list containing None or dicts; passing a string tool name; passing None.

Common situations: Refactoring from a tool-list API and forgetting to wrap a single Tool in a list; mixing tool names (strings) with Tool objects; constructing the toolset before tools are created so the list contains None placeholders.

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/48c58b2aae9a1cf7. Report an issue: GitHub.