BerriAI/litellm · error · ValueError

api_base is required for Pydantic AI agents

Error message

api_base is required for Pydantic AI agents

What it means

PydanticAIHandler.handle_non_streaming() is the lower-level handler that sends the A2A JSON-RPC message to the Pydantic AI agent via PydanticAITransformation.send_non_streaming_request(). It hard-requires api_base because there is no default host for a self-hosted agent; None triggers this ValueError before any HTTP traffic happens. Callers normally get api_base from the model deployment's litellm_params.

Solutions

  1. Configure api_base under litellm_params for the pydantic_ai model in the proxy config
  2. When invoking the handler directly, always pass api_base="http://host:port"
  3. Log the resolved litellm_params before dispatch to confirm api_base survives config parsing
  4. Add a startup check that every pydantic_ai deployment declares api_base

Example fix

# before
resp = await PydanticAIHandler.handle_non_streaming(
    request_id=req_id, params=params, api_base=None, timeout=60.0
)

# after
resp = await PydanticAIHandler.handle_non_streaming(
    request_id=req_id, params=params, api_base="http://localhost:8000", timeout=60.0
)
)
Defensive patterns

Strategy: validation

Validate before calling

async def route_pydantic_ai(params: dict, request_id: str, kwargs: dict):
    api_base = kwargs.get("api_base")
    if api_base is None:
        raise ValueError("api_base is required for Pydantic AI agents")
    return await PydanticAIHandler.handle_non_streaming(
        request_id=request_id, params=params, api_base=api_base
    )

Try / catch

try:
    resp = await PydanticAIHandler.handle_non_streaming(req_id, params, api_base)
except ValueError as e:
    if "api_base is required" in str(e):
        log.error("deployment config incomplete: %s", e)
        raise
    raise

Prevention

When it happens

Trigger: Directly calling PydanticAIHandler.handle_non_streaming(request_id, params, api_base=None); a pydantic_ai deployment in the proxy without api_base in litellm_params so the router passes None down to the handler.

Common situations: Writing custom A2A proxy code that constructs handler arguments manually and omitting api_base; a config.yaml typo (api-base, base_url) that leaves the real key unset; migrating a deployment between environments and dropping the agent URL.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/0453319c83cf613b. Report an issue: GitHub.

Appendix: source

Thrown at litellm/a2a_protocol/providers/pydantic_ai_agents/handler.py:49

        timeout: float = 60.0,
        agent_extra_headers: dict[str, str] | None = None,
    ) -> dict[str, Any]:
        """
        Handle non-streaming request to Pydantic AI agent.

        Args:
            request_id: A2A JSON-RPC request ID
            params: A2A MessageSendParams containing the message
            api_base: Base URL of the Pydantic AI agent
            timeout: Request timeout in seconds
            agent_extra_headers: Per-request headers (from x-a2a-{agent}-* rewrite and
                admin extra_headers) to forward on the upstream HTTP call.

        Returns:
            A2A SendMessageResponse dict
        """
        if api_base is None:
            raise ValueError("api_base is required for Pydantic AI agents")
        verbose_logger.info("Pydantic AI: Routing to Pydantic AI agent at %s", api_base)

        # Send request directly to Pydantic AI agent
        response_data: Final = await PydanticAITransformation.send_non_streaming_request(
            api_base=api_base,
            request_id=request_id,
            params=params,
            timeout=timeout,
            agent_extra_headers=agent_extra_headers,
        )

        return response_data

    @staticmethod
    async def handle_streaming(
        request_id: str,
        params: dict[str, Any],
        api_base: str | None = None,

View on GitHub (pinned to 77b7c6c40c)