BerriAI/litellm · error · UnauthorizedError

{e}

Error message

{e}

What it means

Raised by ModelsManagementClient.list() when GET {base_url}/models returns HTTP 401, converted to UnauthorizedError (redacted message; original on .orig_exception; other non-2xx re-raise as plain requests HTTPError). /models is the OpenAI-style model list that many proxies leave public, so this 401 specifically means your proxy enforces auth on it (master key / strict auth configured) and the client's Bearer credentials were absent or rejected.

Source

Thrown at litellm/proxy/client/models.py:63

        Raises:
            UnauthorizedError: If the request fails with a 401 status code
            requests.exceptions.RequestException: If the request fails with any other error
        """
        url: Final = f"{self._base_url}/models"
        request: Final = requests.Request("GET", url, headers=self._get_headers())

        if return_request:
            return request

        # Prepare and send the request
        session: Final = requests.Session()
        try:
            response: Final = session.send(request.prepare())
            response.raise_for_status()
            return response.json()["data"]
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 401:
                raise UnauthorizedError(e)
            raise

    def new(
        self,
        model_name: str,
        model_params: dict[str, Any],
        model_info: dict[str, Any] | None = None,
        return_request: bool = False,
    ) -> dict[str, Any] | requests.Request:
        """
        Add a new model to the proxy.

        Args:
            model_name (str): Name of the model to add
            model_params (Dict[str, Any]): Parameters for the model (e.g., model type, api_base, api_key)
            model_info (Optional[Dict[str, Any]]): Additional information about the model
            return_request (bool): If True, returns the prepared request object instead of executing it

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Pass a valid key: ModelsManagementClient(base_url, api_key=os.environ["LITELLM_MASTER_KEY"]).list()
  2. Confirm enforcement expectations: curl $BASE_URL/models (no auth) vs curl -H "Authorization: Bearer $KEY" $BASE_URL/models
  3. Rotate/refresh the key if the proxy rejects it
  4. Check base_url — a wrong port can hit a different service that answers 401

Example fix

# before
from litellm.proxy.client.models import ModelsManagementClient
models = ModelsManagementClient("http://localhost:4000")
models.list()  # UnauthorizedError

# after
import os
models = ModelsManagementClient("http://localhost:4000", api_key=os.environ["LITELLM_MASTER_KEY"])
models.list()
Defensive patterns

Strategy: try-catch

Validate before calling

import requests

def models_listable(base_url: str, api_key: str | None) -> bool:
    r = requests.get(
        f"{base_url.rstrip('/')}/models",
        headers={"Authorization": f"Bearer {api_key}"} if api_key else {},
        timeout=10,
    )
    return r.status_code != 401

Try / catch

from litellm.proxy.client.exceptions import UnauthorizedError
import requests

try:
    models = client.list()
except UnauthorizedError:
    raise RuntimeError("proxy requires auth for /models — supply a valid key") from None
except requests.exceptions.HTTPError as e:
    handle(e.response)  # 5xx etc.

Prevention

When it happens

Trigger: Calling models.list() with api_key=None against a proxy configured with a master_key or otherwise requiring auth; a wrong/rotated key; a virtual key the proxy refuses for this route.

Common situations: Code written against an open dev proxy moved to a hardened prod proxy; env var with the key not set in the new deployment; key rotated during incident response.

Related errors


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