BerriAI/litellm · error · FileNotFoundError
OpenAPI spec not found at {filepath}
Error message
OpenAPI spec not found at {filepath} What it means
load_openapi_spec_async raises FileNotFoundError when the configured spec location is not an http(s) URL and no local file exists at that path. It means an MCP server registered with a file-based OpenAPI spec (spec path in mcp_servers config or via the registry) points at a file the proxy process cannot see from its working directory.
Source
Thrown at litellm/proxy/_experimental/mcp_server/openapi_to_mcp_generator.py:157
)
except RuntimeError as e:
# "no running event loop" is fine; other RuntimeErrors we re-raise
if "no running event loop" not in str(e).lower():
raise
return asyncio.run(load_openapi_spec_async(filepath))
async def load_openapi_spec_async(filepath: str) -> dict[str, Any]:
if filepath.startswith("http://") or filepath.startswith("https://"):
client: Final = get_async_httpx_client(llm_provider=httpxSpecialProvider.MCP)
r: Final[httpx.Response] = await async_safe_get(client, filepath)
r.raise_for_status()
return r.json()
# fallback: local file
# Local filesystem path
if not os.path.exists(filepath):
raise FileNotFoundError(f"OpenAPI spec not found at {filepath}")
with open(filepath, "r", encoding="utf-8") as f:
return json.load(f)
def get_base_url(spec: Mapping[str, Any], spec_path: str | None = None) -> str:
"""Extract base URL from OpenAPI spec."""
# OpenAPI 3.x
if "servers" in spec and spec["servers"]:
server_url: Final[str] = spec["servers"][0]["url"]
# If the server URL is relative (starts with /), derive base from spec_path
if server_url.startswith("/") and spec_path:
if spec_path.startswith("http://") or spec_path.startswith("https://"):
# Extract base URL from spec_path (e.g., https://petstore3.swagger.io/api/v3/openapi.json)
# Combine domain with the relative server URL
from urllib.parse import urlparse
parsed: Final = urlparse(spec_path)View on GitHub (pinned to 77b7c6c40c)
Solutions
- Use an absolute filesystem path for the spec file (or switch to an http(s) URL, which is fetched with the shared MCP httpx client instead).
- If containerized, mount the spec into the container and reference the in-container absolute path.
- Verify from the proxy's own cwd: python -c "import os; print(os.path.exists('YOUR_PATH'))" run in the same directory/service the proxy runs in; also strip any file:// prefix and check for typos.
Example fix
# before (config.yaml)
mcp_servers:
myapi:
url: https://api.internal
spec_path: ./openapi.json # relative to proxy cwd -> FileNotFoundError
# after
mcp_servers:
myapi:
url: https://api.internal
spec_path: /etc/litellm/specs/openapi.json # absolute, mounted into the container Defensive patterns
Strategy: validation
Validate before calling
import os
from urllib.parse import urlparse
def spec_source_ok(spec_path: str) -> bool:
if spec_path.startswith(("http://", "https://")):
return urlparse(spec_path).scheme in ("http", "https")
return not spec_path.startswith("file://") and os.path.isabs(spec_path) and os.path.exists(spec_path) Try / catch
from litellm.proxy._experimental.mcp_server.openapi_to_mcp_generator import load_openapi_spec_async
try:
spec = await load_openapi_spec_async(spec_path)
except FileNotFoundError as e:
raise RuntimeError(f"Spec not found; check mount/cwd: {e}") from e Prevention
- Always use absolute spec paths (or http(s) URLs) in config.yaml.
- In containers, mount the spec directory and reference the in-container path; add a startup health check that os.path.exists the spec.
- Watch proxy startup logs for registration errors instead of discovering the missing spec on the first tool call.
When it happens
Trigger: Defining an MCP server with a relative spec path like ./specs/api.json (resolved from the proxy's cwd, not the config file's directory); a typo'd path; a file:// URL (not stripped, treated as a literal filename); a container image that does not mount or copy the spec file.
Common situations: Docker/k8s deployments that mount the config but not the spec directory; running the proxy from a different cwd during debugging vs. systemd; CI pipelines generating specs to a path the runtime stage doesn't include; specs moved/renamed without updating config.
Related errors
- Path parameter '{param_name}' must not contain path separato
- Path parameter '{param_name}' cannot include '.' or '..' seg
- error.summary
- ip_filtering
- server_not_found
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/7ed10fa420f63681.
Report an issue: GitHub.