{"record":{"id":"52c955df9095cbfd","repo":"crewAIInc/crewAI","slug":"invalid-search-type-search-type-must-be-one-of","errorCode":null,"errorMessage":"Invalid search type: {search_type}. Must be one of: {', '.join(allowed_search_types)}","messagePattern":"Invalid search type: (.+?)\\. Must be one of: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/serper_dev_tool/serper_dev_tool.py","lineNumber":137,"sourceCode":"    save_file: bool = False\n    search_type: str = \"search\"\n    country: str | None = \"\"\n    location: str | None = \"\"\n    locale: str | None = \"\"\n    env_vars: list[EnvVar] = Field(\n        default_factory=lambda: [\n            EnvVar(\n                name=\"SERPER_API_KEY\", description=\"API key for Serper\", required=True\n            ),\n        ]\n    )\n\n    def _get_search_url(self, search_type: str) -> str:\n        \"\"\"Get the appropriate endpoint URL based on search type.\"\"\"\n        search_type = search_type.lower()\n        allowed_search_types = [\"search\", \"news\"]\n        if search_type not in allowed_search_types:\n            raise ValueError(\n                f\"Invalid search type: {search_type}. Must be one of: {', '.join(allowed_search_types)}\"\n            )\n        return f\"{self.base_url}/{search_type}\"\n\n    @staticmethod\n    def _process_knowledge_graph(kg: dict[str, Any]) -> KnowledgeGraph:\n        \"\"\"Process knowledge graph data from search results.\"\"\"\n        return {\n            \"title\": kg.get(\"title\", \"\"),\n            \"type\": kg.get(\"type\", \"\"),\n            \"website\": kg.get(\"website\", \"\"),\n            \"imageUrl\": kg.get(\"imageUrl\", \"\"),\n            \"description\": kg.get(\"description\", \"\"),\n            \"descriptionSource\": kg.get(\"descriptionSource\", \"\"),\n            \"descriptionLink\": kg.get(\"descriptionLink\", \"\"),\n            \"attributes\": kg.get(\"attributes\", {}),\n        }\n","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/serper_dev_tool/serper_dev_tool.py#L119-L155","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set search_type to \"search\" (general web search) or \"news\" (news search) — these are the only supported values.","If you passed search_type at _run() call time, remove the argument so the tool falls back to the instance default (\"search\").","If you need other Serper capabilities (images, places), subclass the tool and extend allowed_search_types plus the result-processing branches."],"exampleFix":"# before\ntool = SerperDevTool(search_type=\"images\")\n\n# after\ntool = SerperDevTool(search_type=\"search\")","handlingStrategy":"validation","validationCode":"ALLOWED = {\"search\", \"news\"}\nsearch_type = (search_type or \"search\").lower()\nif search_type not in ALLOWED:\n    raise ValueError(f\"Unsupported search_type {search_type!r}; use {sorted(ALLOWED)}\")\ntool = SerperDevTool(search_type=search_type)","typeGuard":"def is_valid_serper_search_type(t: str) -> bool:\n    return isinstance(t, str) and t.strip().lower() in {\"search\", \"news\"}","tryCatchPattern":"try:\n    result = serper_tool._run(search_query=q, search_type=st)\nexcept ValueError as e:\n    if \"Invalid search type\" in str(e):\n        result = serper_tool._run(search_query=q, search_type=\"search\")","preventionTips":["Whitelist search_type at your configuration boundary before passing it to SerperDevTool.","For agent-driven calls, restrict the search_type argument in the tool schema to an enum of 'search' and 'news'."],"tags":["serper","search","validation","configuration"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}