{"record":{"id":"d3f08ff812f9b0ff","repo":"BerriAI/litellm","slug":"e-d3f08f","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"exception","errorClass":"UnauthorizedError","httpStatus":401,"severity":"error","filePath":"litellm/proxy/client/credentials.py","lineNumber":64,"sourceCode":"        Raises:\n            UnauthorizedError: If the request fails with a 401 status code\n            requests.exceptions.RequestException: If the request fails with any other error\n        \"\"\"\n        url: Final = f\"{self._base_url}/credentials\"\n\n        request: Final = requests.Request(\"GET\", url, headers=self._get_headers())\n\n        if return_request:\n            return request\n\n        session: Final = requests.Session()\n        try:\n            response: Final = session.send(request.prepare())\n            response.raise_for_status()\n            return response.json()\n        except requests.exceptions.HTTPError as e:\n            if e.response.status_code == 401:\n                raise UnauthorizedError(e)\n            raise\n\n    def create(\n        self,\n        credential_name: str,\n        credential_info: dict[str, Any],\n        credential_values: dict[str, Any],\n        return_request: bool = False,\n    ) -> dict[str, Any] | requests.Request:\n        \"\"\"\n        Create a new credential.\n\n        Args:\n            credential_name (str): Name of the credential\n            credential_info (Dict[str, Any]): Additional information about the credential\n            credential_values (Dict[str, Any]): Values for the credential\n            return_request (bool): If True, returns the prepared request object instead of executing it\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/client/credentials.py#L46-L82","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nfrom litellm.proxy.client.credentials import CredentialsManagementClient\nclient = CredentialsManagementClient(\"http://localhost:4000\")\ncreds = client.credentials.list() if hasattr(client, \"credentials\") else client.list()  # UnauthorizedError\n\n# after\nimport os\nfrom litellm.proxy.client.credentials import CredentialsManagementClient\nclient = CredentialsManagementClient(\"http://localhost:4000\", api_key=os.environ[\"LITELLM_MASTER_KEY\"])\ncreds = client.list()","handlingStrategy":"try-catch","validationCode":"import os, requests\n\ndef assert_credentials_access(base_url: str, api_key: str | None) -> None:\n    if not api_key:\n        raise ValueError(\"api_key is required — LiteLLM /credentials is admin-only\")\n    r = requests.get(\n        f\"{base_url.rstrip('/')}/credentials\",\n        headers={\"Authorization\": f\"Bearer {api_key}\"},\n        timeout=10,\n    )\n    if r.status_code == 401:\n        raise ValueError(\"proxy rejected the key (401) — check/rotate the admin key\")","typeGuard":null,"tryCatchPattern":"from litellm.proxy.client.exceptions import UnauthorizedError\nimport requests\n\ntry:\n    creds = client.list()\nexcept UnauthorizedError as e:\n    raise RuntimeError(f\"credentials rejected (401): {e}\") from e  # fix key; do not blind-retry\nexcept requests.exceptions.HTTPError as e:\n    status = e.response.status_code if e.response is not None else None\n    handle_other(status, e)  # 403 = wrong role, 404/500 = server-side","preventionTips":["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"],"tags":["litellm","authentication","http-401","python","credentials"],"backgroundTag":"http-401-unauthorized","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}