BerriAI/litellm · error · ValueError

GOOGLE_PSE_ENGINE_ID is required

Error message

GOOGLE_PSE_ENGINE_ID is required

What it means

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.

Source

Thrown at litellm/llms/google_pse/search/transformation.py:178

            # Google PSE only supports single string queries
            query = " ".join(query)

        # Get API credentials. The key is sent as a query param to api_base, so
        # resolve it host-aware to avoid leaking a server-managed key to a
        # caller-supplied host.
        api_key = self.resolve_server_api_key(
            caller_api_key=api_key,
            caller_api_base=api_base,
            key_env_vars=("GOOGLE_PSE_API_KEY",),
            base_env_var="GOOGLE_PSE_API_BASE",
            default_api_base=self.GOOGLE_PSE_API_BASE,
        )
        search_engine_id = search_engine_id or get_secret_str("GOOGLE_PSE_ENGINE_ID")

        if not api_key:
            raise ValueError("GOOGLE_PSE_API_KEY is required")
        if not search_engine_id:
            raise ValueError("GOOGLE_PSE_ENGINE_ID is required")

        request_data: Final[GooglePSESearchRequest] = {
            "q": query,
            "cx": search_engine_id,
            "key": api_key,
        }

        # Transform unified spec parameters to Google PSE format
        if "max_results" in optional_params:
            # Google PSE supports 1-10 results per request
            num_results: Final = min(optional_params["max_results"], 10)
            request_data["num"] = num_results

        if "search_domain_filter" in optional_params:
            # Convert list to single domain (take first if multiple)
            domains: Final = optional_params["search_domain_filter"]
            if isinstance(domains, list) and len(domains) > 0:
                request_data["siteSearch"] = domains[0]

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Set GOOGLE_PSE_ENGINE_ID to the Programmable Search Engine ID (cx) in the runtime environment.
  2. Or pass search_engine_id explicitly on each search call.
  3. Add a startup assertion that both GOOGLE_PSE_API_KEY and GOOGLE_PSE_ENGINE_ID are non-empty before serving search requests.
  4. Verify the value actually reaches litellm (get_secret_str reads env and secret managers — check both sources).

Example fix

# before: kwarg dropped in a refactor -> engine id resolves to None
engine = config.get("engine")  # key is actually 'search_engine_id'
litellm.web_search(provider="google_pse", query="x")

# after: correct kwarg + env fallback asserted at boot
assert os.environ.get("GOOGLE_PSE_ENGINE_ID"), "GOOGLE_PSE_ENGINE_ID required"
litellm.web_search(provider="google_pse", query="x", search_engine_id=engine_id)
Defensive patterns

Strategy: validation

Validate before calling

import os

def require_pse_engine_id(search_engine_id: str | None) -> str:
    engine = search_engine_id or os.getenv("GOOGLE_PSE_ENGINE_ID")
    if not engine:
        raise ValueError("search_engine_id or GOOGLE_PSE_ENGINE_ID is required for google_pse")
    return engine

engine_id = require_pse_engine_id(config.get("search_engine_id"))

Type guard

def has_pse_engine_id(request_engine_id: str | None) -> bool:
    """Narrow a request to a usable google_pse engine ID."""
    import os
    return bool(request_engine_id or os.getenv("GOOGLE_PSE_ENGINE_ID"))

Try / catch

try:
    results = litellm.web_search(provider="google_pse", query=q, search_engine_id=engine_id)
except ValueError as e:
    if "GOOGLE_PSE_ENGINE_ID is required" in str(e):
        engine_id = os.environ["GOOGLE_PSE_ENGINE_ID"]  # load from trusted config, then retry once
        results = litellm.web_search(provider="google_pse", query=q, search_engine_id=engine_id)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/ba5dfa9e38f11f44. Report an issue: GitHub.