BerriAI/litellm · error · UnauthorizedError
{e}
Error message
{e} What it means
Raised by CredentialsManagementClient.list() when GET {base_url}/credentials returns HTTP 401. The client maps every 401 to UnauthorizedError (litellm/proxy/client/exceptions.py); its message is the secret-redacted str of the underlying requests HTTPError (e.g. '401 Client Error: Unauthorized for url: http://localhost:4000/credentials') and the original error is kept on .orig_exception. /credentials is an admin endpoint, so 401 means the request carried no acceptable Bearer key — note the client sends no Authorization header at all when api_key is None (Client only falls back to the stored CLI key when it was issued for the same base_url).
Source
Thrown at litellm/proxy/client/credentials.py:64
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}/credentials"
request: Final = requests.Request("GET", url, headers=self._get_headers())
if return_request:
return request
session: Final = requests.Session()
try:
response: Final = session.send(request.prepare())
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 401:
raise UnauthorizedError(e)
raise
def create(
self,
credential_name: str,
credential_info: dict[str, Any],
credential_values: dict[str, Any],
return_request: bool = False,
) -> dict[str, Any] | requests.Request:
"""
Create a new credential.
Args:
credential_name (str): Name of the credential
credential_info (Dict[str, Any]): Additional information about the credential
credential_values (Dict[str, Any]): Values for the credential
return_request (bool): If True, returns the prepared request object instead of executing it
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Pass an admin key explicitly: CredentialsManagementClient(base_url, api_key=os.environ["LITELLM_MASTER_KEY"]).list() — or Client(base_url, api_key=...).credentials.list()
- Verify the key out-of-band: curl -H "Authorization: Bearer $KEY" $BASE_URL/credentials should return 200, not 401
- If the key was revoked/expired, mint a new admin key on the proxy and update the client configuration
- Double-check base_url scheme/host/port — a valid key for a different server still yields 401 here
Example fix
# before
from litellm.proxy.client.credentials import CredentialsManagementClient
client = CredentialsManagementClient("http://localhost:4000")
creds = client.credentials.list() if hasattr(client, "credentials") else client.list() # UnauthorizedError
# after
import os
from litellm.proxy.client.credentials import CredentialsManagementClient
client = CredentialsManagementClient("http://localhost:4000", api_key=os.environ["LITELLM_MASTER_KEY"])
creds = client.list() Defensive patterns
Strategy: try-catch
Validate before calling
import os, requests
def assert_credentials_access(base_url: str, api_key: str | None) -> None:
if not api_key:
raise ValueError("api_key is required — LiteLLM /credentials is admin-only")
r = requests.get(
f"{base_url.rstrip('/')}/credentials",
headers={"Authorization": f"Bearer {api_key}"},
timeout=10,
)
if r.status_code == 401:
raise ValueError("proxy rejected the key (401) — check/rotate the admin key") Try / catch
from litellm.proxy.client.exceptions import UnauthorizedError
import requests
try:
creds = client.list()
except UnauthorizedError as e:
raise RuntimeError(f"credentials rejected (401): {e}") from e # fix key; do not blind-retry
except requests.exceptions.HTTPError as e:
status = e.response.status_code if e.response is not None else None
handle_other(status, e) # 403 = wrong role, 404/500 = server-side Prevention
- Load the admin key from an env var (LITELLM_MASTER_KEY) instead of literals, and fail fast when unset
- Note that api_key=None (or empty string) sends no Authorization header at all — assert non-empty at startup
- After rotating keys on the proxy, update every client's configuration in the same change
When it happens
Trigger: Constructing the client without api_key against an auth-enforcing proxy (header omitted entirely); passing a virtual/end-user key that lacks admin scope instead of the master key; using a key that was deleted, revoked, expired, or rotated on the proxy.
Common situations: Proxy started with a master_key in config but the script reads the wrong env var (empty string also omits the header); key rotated server-side while hardcoded in client code; base_url pointing at a different environment (staging key vs prod proxy); typos in the key string.
Related errors
- {e}
- {e}
- {e}
- Authentication failed. Check your BitBucket access token and
- Error: Watsonx API key not set. Set WATSONX_API_KEY in envir
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/d3f08ff812f9b0ff.
Report an issue: GitHub.