{"record":{"id":"4eed85e691d9af0f","repo":"BerriAI/litellm","slug":"error-updating-key-response-text","errorCode":null,"errorMessage":"Error updating key: {response_text}","messagePattern":"Error updating key: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/proxy/client/keys.py","lineNumber":287,"sourceCode":"            data[\"team_id\"] = team_id\n        if models is not None:\n            data[\"models\"] = models\n        if spend is not None:\n            data[\"spend\"] = spend\n        if duration is not None:\n            data[\"duration\"] = duration\n        if aliases is not None:\n            data[\"aliases\"] = aliases\n        request: Final = requests.Request(\"POST\", url, headers=self._get_headers(), json=data)\n        session: Final = requests.Session()\n        response_text: str | None = None\n        try:\n            response: Final = session.send(request.prepare())\n            response_text = response.text\n            response.raise_for_status()\n            return response.json()\n        except Exception:\n            raise Exception(f\"Error updating key: {response_text}\")\n\n    def info(self, key: str, return_request: bool = False) -> dict[str, Any] | requests.Request:\n        \"\"\"\n        Get information about API keys.\n\n        Args:\n            key (str): The key hash to get information about\n            return_request (bool): If True, returns the prepared request object instead of executing it\n\n        Returns:\n            Union[Dict[str, Any], requests.Request]: Either the response from the server or a prepared request object if return_request is True\n\n        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}/key/info?key={key}\"\n        request: Final = requests.Request(\"GET\", url, headers=self._get_headers())","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/client/keys.py#L269-L305","documentation":"KeysManagementClient.update() wraps every failure of POST {base_url}/key/update in a bare Exception whose message is 'Error updating key: ' + the raw response body — or the literal 'Error updating key: None' when no response exists (connection refused, DNS failure, timeout). Unlike its sibling methods it catches Exception rather than HTTPError, so all type information is lost: a 401 that would elsewhere become UnauthorizedError surfaces here as this generic exception, and the original traceback is discarded because the raise uses no `from`. To diagnose, you must read the server text embedded in the message.","triggerScenarios":"Updating a key hash that no longer exists (400/404 body inline in the message); proxy unreachable or base_url wrong ('Error updating key: None'); auth rejected (401 body inline); server rejecting duration/spend/models values (400/422 body inline); passing a key alias where the hash is expected.","commonSituations":"Key deleted on the proxy between fetch and update; network flake or wrong port in base_url; rotated admin key; scripts assuming typed exceptions (UnauthorizedError) around update() and missing this generic one.","solutions":["Read the text after the prefix — the proxy's actual error (e.g.{\"error\":\"Key does not exist in proxy\"}) tells you whether it's auth, missing key, or validation","Pre-verify the key with keys.info(key=...) (which raises typed UnauthorizedError/HTTPError) and confirm you pass the key hash, not the alias","If the message ends with 'None', the request never got a response: check the proxy is running and base_url/port are correct","Catch this Exception narrowly around update() and re-raise a typed error, since the library discards the original"],"exampleFix":"# before\nkeys.update(key=\"sk-123\", spend=100)  # Exception: Error updating key: {\"error\": \"Key does not exist in proxy\"}\n\n# after\nfrom litellm.proxy.client.exceptions import UnauthorizedError\ntry:\n    keys.info(key=\"sk-123\")  # typed pre-check: raises UnauthorizedError on bad creds\n    keys.update(key=\"sk-123\", spend=100)\nexcept Exception as e:\n    raise RuntimeError(f\"key update failed: {e}\") from e","handlingStrategy":"validation","validationCode":"from litellm.proxy.client.keys import KeysManagementClient\n\ndef update_key_if_exists(keys: KeysManagementClient, key: str, **fields) -> dict:\n    keys.info(key=key)  # typed pre-check: raises UnauthorizedError for bad creds, HTTPError 404 if gone\n    return keys.update(key=key, **fields)","typeGuard":null,"tryCatchPattern":"try:\n    keys.update(key=key, spend=new_spend)\nexcept Exception as e:  # update() raises a bare Exception — cannot narrow further\n    msg = str(e)\n    if msg.endswith(\"None\"):\n        raise RuntimeError(\"proxy unreachable (no HTTP response)\") from e\n    raise RuntimeError(f\"key update rejected by proxy: {msg}\") from e","preventionTips":["Always pre-verify the key with keys.info() — it raises typed errors — before calling update()","Pass the key hash (sk-...), not the alias, to /key/update","Remember update() collapses ALL failures (including 401 and network errors) into one generic Exception; code around it accordingly"],"tags":["litellm","keys","generic-exception","python","error-handling"],"backgroundTag":"api-request-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}