{"record":{"id":"ba5dfa9e38f11f44","repo":"BerriAI/litellm","slug":"google-pse-engine-id-is-required","errorCode":null,"errorMessage":"GOOGLE_PSE_ENGINE_ID is required","messagePattern":"GOOGLE_PSE_ENGINE_ID is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/google_pse/search/transformation.py","lineNumber":178,"sourceCode":"            # Google PSE only supports single string queries\n            query = \" \".join(query)\n\n        # Get API credentials. The key is sent as a query param to api_base, so\n        # resolve it host-aware to avoid leaking a server-managed key to a\n        # caller-supplied host.\n        api_key = self.resolve_server_api_key(\n            caller_api_key=api_key,\n            caller_api_base=api_base,\n            key_env_vars=(\"GOOGLE_PSE_API_KEY\",),\n            base_env_var=\"GOOGLE_PSE_API_BASE\",\n            default_api_base=self.GOOGLE_PSE_API_BASE,\n        )\n        search_engine_id = search_engine_id or get_secret_str(\"GOOGLE_PSE_ENGINE_ID\")\n\n        if not api_key:\n            raise ValueError(\"GOOGLE_PSE_API_KEY is required\")\n        if not search_engine_id:\n            raise ValueError(\"GOOGLE_PSE_ENGINE_ID is required\")\n\n        request_data: Final[GooglePSESearchRequest] = {\n            \"q\": query,\n            \"cx\": search_engine_id,\n            \"key\": api_key,\n        }\n\n        # Transform unified spec parameters to Google PSE format\n        if \"max_results\" in optional_params:\n            # Google PSE supports 1-10 results per request\n            num_results: Final = min(optional_params[\"max_results\"], 10)\n            request_data[\"num\"] = num_results\n\n        if \"search_domain_filter\" in optional_params:\n            # Convert list to single domain (take first if multiple)\n            domains: Final = optional_params[\"search_domain_filter\"]\n            if isinstance(domains, list) and len(domains) > 0:\n                request_data[\"siteSearch\"] = domains[0]","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/google_pse/search/transformation.py#L160-L196","documentation":"Raised in the google_pse search-execution path when search_engine_id is still falsy after checking the per-request parameter and the GOOGLE_PSE_ENGINE_ID secret. Same missing-cx failure as error 1637, raised at request-build time with the shorter 'is required' message. It fires only after the API key check passes, so seeing it means key resolution succeeded.","triggerScenarios":"Building a google_pse search request with GOOGLE_PSE_API_KEY present but neither a search_engine_id argument nor GOOGLE_PSE_ENGINE_ID in the environment/secrets; e.g. per-request code that conditionally passes the engine ID and takes the wrong branch.","commonSituations":"Refactors that rename the kwarg (search_engine_id vs engine_id) so None is passed; multi-tenant setups where engine IDs live in a database but a null tenant slips through; deployments that fixed the key error and stopped before adding the engine secret.","solutions":["Set GOOGLE_PSE_ENGINE_ID to the Programmable Search Engine ID (cx) in the runtime environment.","Or pass search_engine_id explicitly on each search call.","Add a startup assertion that both GOOGLE_PSE_API_KEY and GOOGLE_PSE_ENGINE_ID are non-empty before serving search requests.","Verify the value actually reaches litellm (get_secret_str reads env and secret managers — check both sources)."],"exampleFix":"# before: kwarg dropped in a refactor -> engine id resolves to None\nengine = config.get(\"engine\")  # key is actually 'search_engine_id'\nlitellm.web_search(provider=\"google_pse\", query=\"x\")\n\n# after: correct kwarg + env fallback asserted at boot\nassert os.environ.get(\"GOOGLE_PSE_ENGINE_ID\"), \"GOOGLE_PSE_ENGINE_ID required\"\nlitellm.web_search(provider=\"google_pse\", query=\"x\", search_engine_id=engine_id)","handlingStrategy":"validation","validationCode":"import os\n\ndef require_pse_engine_id(search_engine_id: str | None) -> str:\n    engine = search_engine_id or os.getenv(\"GOOGLE_PSE_ENGINE_ID\")\n    if not engine:\n        raise ValueError(\"search_engine_id or GOOGLE_PSE_ENGINE_ID is required for google_pse\")\n    return engine\n\nengine_id = require_pse_engine_id(config.get(\"search_engine_id\"))","typeGuard":"def has_pse_engine_id(request_engine_id: str | None) -> bool:\n    \"\"\"Narrow a request to a usable google_pse engine ID.\"\"\"\n    import os\n    return bool(request_engine_id or os.getenv(\"GOOGLE_PSE_ENGINE_ID\"))","tryCatchPattern":"try:\n    results = litellm.web_search(provider=\"google_pse\", query=q, search_engine_id=engine_id)\nexcept ValueError as e:\n    if \"GOOGLE_PSE_ENGINE_ID is required\" in str(e):\n        engine_id = os.environ[\"GOOGLE_PSE_ENGINE_ID\"]  # load from trusted config, then retry once\n        results = litellm.web_search(provider=\"google_pse\", query=q, search_engine_id=engine_id)\n    else:\n        raise","preventionTips":["Centralize engine-ID resolution (env fallback + kwarg) in one helper used by all call sites.","Assert GOOGLE_PSE_ENGINE_ID at startup alongside the API key.","Watch for kwarg renames in refactors (search_engine_id is the litellm name).","For multi-engine setups, validate each tenant's engine ID before request time."],"tags":["google-pse","search","engine-id","configuration","environment"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}