FoundationAgents/MetaGPT · error · ValueError

To use serper search engine, make sure you provide the `api_

Error message

To use serper search engine, make sure you provide the `api_key` when constructing an object. You can obtain an API key from https://serper.dev/.

What it means

ValueError from SerperWrapper's model_validator (mode='before'): constructor values contain neither api_key nor the deprecated serper_api_key alias. Serper.dev requires an API key, so instantiation fails fast before any HTTP call. The legacy alias is accepted with a DeprecationWarning.

Source

Thrown at metagpt/tools/search_engine_serper.py:33

class SerperWrapper(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    api_key: str
    url: str = "https://google.serper.dev/search"
    payload: dict = Field(default_factory=lambda: {"page": 1, "num": 10})
    aiosession: Optional[aiohttp.ClientSession] = None
    proxy: Optional[str] = None

    @model_validator(mode="before")
    @classmethod
    def validate_serper(cls, values: dict) -> dict:
        if "serper_api_key" in values:
            values.setdefault("api_key", values["serper_api_key"])
            warnings.warn("`serper_api_key` is deprecated, use `api_key` instead", DeprecationWarning, stacklevel=2)

        if "api_key" not in values:
            raise ValueError(
                "To use serper search engine, make sure you provide the `api_key` when constructing an object. You can obtain "
                "an API key from https://serper.dev/."
            )

        return values

    async def run(self, query: str, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str:
        """Run query through Serper and parse result async."""
        if isinstance(query, str):
            return self._process_response((await self.results([query], max_results))[0], as_string=as_string)
        else:
            results = [self._process_response(res, as_string) for res in await self.results(query, max_results)]
        return "\n".join(results) if as_string else results

    async def results(self, queries: list[str], max_results: int = 8) -> dict:
        """Use aiohttp to run query through Serper and return the results async."""

        payloads = self.get_payloads(queries, max_results)

View on GitHub (pinned to 11cdf466d0)

Solutions

  1. Pass the key: SerperWrapper(api_key=os.environ['SERPER_API_KEY'])
  2. Or set api_key under the search engine config in your MetaGPT yaml
  3. Legacy serper_api_key field still works if present in old configs

Example fix

# before
engine = SerperWrapper()  # ValueError
# after
engine = SerperWrapper(api_key=os.environ["SERPER_API_KEY"])
Defensive patterns

Strategy: validation

Validate before calling

import os
api_key = os.environ.get("SERPER_API_KEY")
if not api_key:
    raise SystemExit("set SERPER_API_KEY before constructing SerperWrapper")

Try / catch

try:
    engine = SerperWrapper(api_key=k)
except ValueError as e:
    raise ConfigError(str(e))

Prevention

When it happens

Trigger: SerperWrapper() with no args; MetaGPT config selects the serper engine without an api_key; key expected from env var that isn't set in the runtime shell.

Common situations: Switching SearchEngine to serper without configuring SERPER_API_KEY; .env omitted from deployment; config migrated to new field names incompletely.

Related errors


AI-assisted analysis of FoundationAgents/MetaGPT@11cdf466d0 (2026-08-14). Data as JSON: /api/errors/2d9ea33b3a5d12c9. Report an issue: GitHub.