crewAIInc/crewAI · error · ValueError

Invalid search type: {search_type}. Must be one of: {', '.jo

Error message

Invalid search type: {search_type}. Must be one of: {', '.join(allowed_search_types)}

What it means

SerperDevTool raises this ValueError from _get_search_url when the requested search_type, lowercased, is not in the hard-coded allowlist ["search", "news"]. The tool only supports two Serper endpoints: the general /search endpoint and the /news endpoint. Any other value (e.g. 'images', 'scholar', 'places') is rejected before any HTTP request is made.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/serper_dev_tool/serper_dev_tool.py:137

    save_file: bool = False
    search_type: str = "search"
    country: str | None = ""
    location: str | None = ""
    locale: str | None = ""
    env_vars: list[EnvVar] = Field(
        default_factory=lambda: [
            EnvVar(
                name="SERPER_API_KEY", description="API key for Serper", required=True
            ),
        ]
    )

    def _get_search_url(self, search_type: str) -> str:
        """Get the appropriate endpoint URL based on search type."""
        search_type = search_type.lower()
        allowed_search_types = ["search", "news"]
        if search_type not in allowed_search_types:
            raise ValueError(
                f"Invalid search type: {search_type}. Must be one of: {', '.join(allowed_search_types)}"
            )
        return f"{self.base_url}/{search_type}"

    @staticmethod
    def _process_knowledge_graph(kg: dict[str, Any]) -> KnowledgeGraph:
        """Process knowledge graph data from search results."""
        return {
            "title": kg.get("title", ""),
            "type": kg.get("type", ""),
            "website": kg.get("website", ""),
            "imageUrl": kg.get("imageUrl", ""),
            "description": kg.get("description", ""),
            "descriptionSource": kg.get("descriptionSource", ""),
            "descriptionLink": kg.get("descriptionLink", ""),
            "attributes": kg.get("attributes", {}),
        }

View on GitHub (pinned to 754d7323be)

Solutions

  1. Set search_type to "search" (general web search) or "news" (news search) — these are the only supported values.
  2. If you passed search_type at _run() call time, remove the argument so the tool falls back to the instance default ("search").
  3. If you need other Serper capabilities (images, places), subclass the tool and extend allowed_search_types plus the result-processing branches.

Example fix

# before
tool = SerperDevTool(search_type="images")

# after
tool = SerperDevTool(search_type="search")
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {"search", "news"}
search_type = (search_type or "search").lower()
if search_type not in ALLOWED:
    raise ValueError(f"Unsupported search_type {search_type!r}; use {sorted(ALLOWED)}")
tool = SerperDevTool(search_type=search_type)

Type guard

def is_valid_serper_search_type(t: str) -> bool:
    return isinstance(t, str) and t.strip().lower() in {"search", "news"}

Try / catch

try:
    result = serper_tool._run(search_query=q, search_type=st)
except ValueError as e:
    if "Invalid search type" in str(e):
        result = serper_tool._run(search_query=q, search_type="search")

Prevention

When it happens

Trigger: Constructing SerperDevTool(search_type="images") or any value other than "search"/"news"; or calling _run(search_query=..., search_type="videos") at execution time; even casing variants like "NEWS" are fine because the value is lowercased, but anything else fails.

Common situations: Developers copy search_type names from Serper's website (which offers more endpoints like /images or /places) or from other search tools (GoogleSerperAPIWrapper style values), assuming SerperDevTool supports them. Also LLM agents passing an invented search_type argument at runtime.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/52c955df9095cbfd. Report an issue: GitHub.