{"record":{"id":"25232ca07514169e","repo":"BerriAI/litellm","slug":"api-base-is-required-for-pydanticaiproviderconfig","errorCode":null,"errorMessage":"api_base is required for PydanticAIProviderConfig","messagePattern":"api_base is required for PydanticAIProviderConfig","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/a2a_protocol/providers/pydantic_ai_agents/config.py","lineNumber":29,"sourceCode":"\nclass PydanticAIProviderConfig(BaseA2AProviderConfig):\n    \"\"\"\n    Provider configuration for Pydantic AI agents.\n\n    Pydantic AI agents follow A2A protocol but don't support streaming natively.\n    This config provides fake streaming by converting non-streaming responses into streaming chunks.\n    \"\"\"\n\n    async def handle_non_streaming(\n        self,\n        request_id: str,\n        params: dict[str, Any],\n        api_base: str | None = None,\n        **kwargs: Any,\n    ) -> dict[str, Any]:\n        \"\"\"Handle non-streaming request to Pydantic AI agent.\"\"\"\n        if api_base is None:\n            raise ValueError(\"api_base is required for PydanticAIProviderConfig\")\n        return await PydanticAIHandler.handle_non_streaming(\n            request_id=request_id,\n            params=params,\n            api_base=api_base,\n            timeout=kwargs.get(\"timeout\", 60.0),\n            agent_extra_headers=kwargs.get(\"agent_extra_headers\"),\n        )\n\n    async def handle_streaming(\n        self,\n        request_id: str,\n        params: dict[str, Any],\n        api_base: str | None = None,\n        **kwargs,\n    ) -> AsyncIterator[dict[str, Any]]:\n        \"\"\"Handle streaming request with fake streaming.\"\"\"\n        if not api_base:\n            raise ValueError(\"api_base is required for Pydantic AI agents\")","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/a2a_protocol/providers/pydantic_ai_agents/config.py#L11-L47","documentation":"PydanticAIProviderConfig.handle_non_streaming() forwards a request to a self-hosted Pydantic AI agent over HTTP and needs the agent's base URL to do so. If api_base arrives as None (it is optional in the signature), the config layer refuses to build the upstream call and raises this ValueError. In the LiteLLM A2A (agent-to-agent) routing layer, api_base comes from the deployment's litellm_params, so this almost always means the model entry for the pydantic_ai agent never configured one.","triggerScenarios":"Calling litellm.completion()/acompletion() (or the A2A proxy route) with a model whose provider is pydantic_ai while the deployment/config has no api_base in litellm_params; invoking PydanticAIProviderConfig.handle_non_streaming(request_id, params) directly without api_base; passing api_base=None explicitly.","commonSituations":"Adding a Pydantic AI agent to a LiteLLM proxy model_list and forgetting the api_base line; copying an OpenAI-style config (api_key only) for a pydantic_ai deployment; renaming the param to base_url or url so it is silently ignored; the agent deployment YAML indentation putting api_base outside litellm_params.","solutions":["Add api_base to the deployment's litellm_params, e.g. litellm_params: {model: pydantic_ai/my-agent, api_base: http://localhost:8000}","If calling programmatically, pass api_base=\"http://host:port\" to the completion call for the pydantic_ai model","Verify the param name is exactly api_base (not base_url / url) and that it sits inside litellm_params in config.yaml","Confirm the Pydantic AI agent server is actually deployed at that URL and reachable from the proxy"],"exampleFix":"# before\nresponse = litellm.completion(\n    model=\"pydantic_ai/my-agent\",\n    messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n)\n\n# after\nresponse = litellm.completion(\n    model=\"pydantic_ai/my-agent\",\n    messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n    api_base=\"http://localhost:8000\",\n)","handlingStrategy":"validation","validationCode":"from litellm import Router\n\ndef assert_pydantic_ai_deployment_ready(deployment: dict) -> None:\n    lp = deployment.get(\"litellm_params\", {})\n    if deployment[\"litellm_params\"].get(\"model\", \"\").startswith(\"pydantic_ai\"):\n        if not lp.get(\"api_base\"):\n            raise ValueError(\n                f\"Deployment {deployment.get('model_name')} is missing api_base\"\n            )\n","typeGuard":"def has_api_base(params: dict) -> bool:\n    \"\"\"True when params carry a usable api_base for pydantic_ai routing.\"\"\"\n    return isinstance(params.get(\"api_base\"), str) and len(params[\"api_base\"].strip()) > 0\n","tryCatchPattern":"try:\n    result = await provider_config.handle_non_streaming(request_id, params, api_base=api_base)\nexcept ValueError as e:\n    if \"api_base is required\" in str(e):\n        raise HTTPException(500, \"Agent deployment missing api_base\") from e\n    raise\n","preventionTips":["Lint config.yaml at deploy time: every pydantic_ai deployment must set api_base","Use env-var indirection with a startup check that fails fast when unset","Keep one canonical config template for pydantic_ai agents that includes api_base"],"tags":["pydantic-ai","a2a","api-base","configuration","provider"],"backgroundTag":"missing-required-argument","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}