Spaces:
Runtime error
Runtime error
import re | |
from typing import Optional | |
from requests import HTTPError, Response | |
from ._fixes import JSONDecodeError | |
REPO_API_REGEX = re.compile( | |
r""" | |
# staging or production endpoint | |
^https://[^/]+ | |
( | |
# on /api/repo_type/repo_id | |
/api/(models|datasets|spaces)/(.+) | |
| | |
# or /repo_id/resolve/revision/... | |
/(.+)/resolve/(.+) | |
) | |
""", | |
flags=re.VERBOSE, | |
) | |
class FileMetadataError(OSError): | |
"""Error triggered when the metadata of a file on the Hub cannot be retrieved (missing ETag or commit_hash). | |
Inherits from `OSError` for backward compatibility. | |
""" | |
class HfHubHTTPError(HTTPError): | |
""" | |
HTTPError to inherit from for any custom HTTP Error raised in HF Hub. | |
Any HTTPError is converted at least into a `HfHubHTTPError`. If some information is | |
sent back by the server, it will be added to the error message. | |
Added details: | |
- Request id from "X-Request-Id" header if exists. | |
- Server error message from the header "X-Error-Message". | |
- Server error message if we can found one in the response body. | |
Example: | |
```py | |
import requests | |
from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError | |
response = get_session().post(...) | |
try: | |
hf_raise_for_status(response) | |
except HfHubHTTPError as e: | |
print(str(e)) # formatted message | |
e.request_id, e.server_message # details returned by server | |
# Complete the error message with additional information once it's raised | |
e.append_to_message("\n`create_commit` expects the repository to exist.") | |
raise | |
``` | |
""" | |
request_id: Optional[str] = None | |
server_message: Optional[str] = None | |
def __init__(self, message: str, response: Optional[Response] = None): | |
# Parse server information if any. | |
if response is not None: | |
self.request_id = response.headers.get("X-Request-Id") | |
try: | |
server_data = response.json() | |
except JSONDecodeError: | |
server_data = {} | |
# Retrieve server error message from multiple sources | |
server_message_from_headers = response.headers.get("X-Error-Message") | |
server_message_from_body = server_data.get("error") | |
server_multiple_messages_from_body = "\n".join( | |
error["message"] for error in server_data.get("errors", []) if "message" in error | |
) | |
# Concatenate error messages | |
_server_message = "" | |
if server_message_from_headers is not None: # from headers | |
_server_message += server_message_from_headers + "\n" | |
if server_message_from_body is not None: # from body "error" | |
if isinstance(server_message_from_body, list): | |
server_message_from_body = "\n".join(server_message_from_body) | |
if server_message_from_body not in _server_message: | |
_server_message += server_message_from_body + "\n" | |
if server_multiple_messages_from_body is not None: # from body "errors" | |
if server_multiple_messages_from_body not in _server_message: | |
_server_message += server_multiple_messages_from_body + "\n" | |
_server_message = _server_message.strip() | |
# Set message to `HfHubHTTPError` (if any) | |
if _server_message != "": | |
self.server_message = _server_message | |
super().__init__( | |
_format_error_message( | |
message, | |
request_id=self.request_id, | |
server_message=self.server_message, | |
), | |
response=response, # type: ignore | |
request=response.request if response is not None else None, # type: ignore | |
) | |
def append_to_message(self, additional_message: str) -> None: | |
"""Append additional information to the `HfHubHTTPError` initial message.""" | |
self.args = (self.args[0] + additional_message,) + self.args[1:] | |
class RepositoryNotFoundError(HfHubHTTPError): | |
""" | |
Raised when trying to access a hf.co URL with an invalid repository name, or | |
with a private repo name the user does not have access to. | |
Example: | |
```py | |
>>> from huggingface_hub import model_info | |
>>> model_info("<non_existent_repository>") | |
(...) | |
huggingface_hub.utils._errors.RepositoryNotFoundError: 401 Client Error. (Request ID: PvMw_VjBMjVdMz53WKIzP) | |
Repository Not Found for url: https://huggingface.co/api/models/%3Cnon_existent_repository%3E. | |
Please make sure you specified the correct `repo_id` and `repo_type`. | |
If the repo is private, make sure you are authenticated. | |
Invalid username or password. | |
``` | |
""" | |
class GatedRepoError(RepositoryNotFoundError): | |
""" | |
Raised when trying to access a gated repository for which the user is not on the | |
authorized list. | |
Note: derives from `RepositoryNotFoundError` to ensure backward compatibility. | |
Example: | |
```py | |
>>> from huggingface_hub import model_info | |
>>> model_info("<gated_repository>") | |
(...) | |
huggingface_hub.utils._errors.GatedRepoError: 403 Client Error. (Request ID: ViT1Bf7O_026LGSQuVqfa) | |
Cannot access gated repo for url https://huggingface.co/api/models/ardent-figment/gated-model. | |
Access to model ardent-figment/gated-model is restricted and you are not in the authorized list. | |
Visit https://huggingface.co/ardent-figment/gated-model to ask for access. | |
``` | |
""" | |
class DisabledRepoError(HfHubHTTPError): | |
""" | |
Raised when trying to access a repository that has been disabled by its author. | |
Example: | |
```py | |
>>> from huggingface_hub import dataset_info | |
>>> dataset_info("laion/laion-art") | |
(...) | |
huggingface_hub.utils._errors.DisabledRepoError: 403 Client Error. (Request ID: Root=1-659fc3fa-3031673e0f92c71a2260dbe2;bc6f4dfb-b30a-4862-af0a-5cfe827610d8) | |
Cannot access repository for url https://huggingface.co/api/datasets/laion/laion-art. | |
Access to this resource is disabled. | |
``` | |
""" | |
class RevisionNotFoundError(HfHubHTTPError): | |
""" | |
Raised when trying to access a hf.co URL with a valid repository but an invalid | |
revision. | |
Example: | |
```py | |
>>> from huggingface_hub import hf_hub_download | |
>>> hf_hub_download('bert-base-cased', 'config.json', revision='<non-existent-revision>') | |
(...) | |
huggingface_hub.utils._errors.RevisionNotFoundError: 404 Client Error. (Request ID: Mwhe_c3Kt650GcdKEFomX) | |
Revision Not Found for url: https://huggingface.co/bert-base-cased/resolve/%3Cnon-existent-revision%3E/config.json. | |
``` | |
""" | |
class EntryNotFoundError(HfHubHTTPError): | |
""" | |
Raised when trying to access a hf.co URL with a valid repository and revision | |
but an invalid filename. | |
Example: | |
```py | |
>>> from huggingface_hub import hf_hub_download | |
>>> hf_hub_download('bert-base-cased', '<non-existent-file>') | |
(...) | |
huggingface_hub.utils._errors.EntryNotFoundError: 404 Client Error. (Request ID: 53pNl6M0MxsnG5Sw8JA6x) | |
Entry Not Found for url: https://huggingface.co/bert-base-cased/resolve/main/%3Cnon-existent-file%3E. | |
``` | |
""" | |
class LocalEntryNotFoundError(EntryNotFoundError, FileNotFoundError, ValueError): | |
""" | |
Raised when trying to access a file or snapshot that is not on the disk when network is | |
disabled or unavailable (connection issue). The entry may exist on the Hub. | |
Note: `ValueError` type is to ensure backward compatibility. | |
Note: `LocalEntryNotFoundError` derives from `HTTPError` because of `EntryNotFoundError` | |
even when it is not a network issue. | |
Example: | |
```py | |
>>> from huggingface_hub import hf_hub_download | |
>>> hf_hub_download('bert-base-cased', '<non-cached-file>', local_files_only=True) | |
(...) | |
huggingface_hub.utils._errors.LocalEntryNotFoundError: Cannot find the requested files in the disk cache and outgoing traffic has been disabled. To enable hf.co look-ups and downloads online, set 'local_files_only' to False. | |
``` | |
""" | |
def __init__(self, message: str): | |
super().__init__(message, response=None) | |
class BadRequestError(HfHubHTTPError, ValueError): | |
""" | |
Raised by `hf_raise_for_status` when the server returns a HTTP 400 error. | |
Example: | |
```py | |
>>> resp = requests.post("hf.co/api/check", ...) | |
>>> hf_raise_for_status(resp, endpoint_name="check") | |
huggingface_hub.utils._errors.BadRequestError: Bad request for check endpoint: {details} (Request ID: XXX) | |
``` | |
""" | |
def hf_raise_for_status(response: Response, endpoint_name: Optional[str] = None) -> None: | |
""" | |
Internal version of `response.raise_for_status()` that will refine a | |
potential HTTPError. Raised exception will be an instance of `HfHubHTTPError`. | |
This helper is meant to be the unique method to raise_for_status when making a call | |
to the Hugging Face Hub. | |
Example: | |
```py | |
import requests | |
from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError | |
response = get_session().post(...) | |
try: | |
hf_raise_for_status(response) | |
except HfHubHTTPError as e: | |
print(str(e)) # formatted message | |
e.request_id, e.server_message # details returned by server | |
# Complete the error message with additional information once it's raised | |
e.append_to_message("\n`create_commit` expects the repository to exist.") | |
raise | |
``` | |
Args: | |
response (`Response`): | |
Response from the server. | |
endpoint_name (`str`, *optional*): | |
Name of the endpoint that has been called. If provided, the error message | |
will be more complete. | |
<Tip warning={true}> | |
Raises when the request has failed: | |
- [`~utils.RepositoryNotFoundError`] | |
If the repository to download from cannot be found. This may be because it | |
doesn't exist, because `repo_type` is not set correctly, or because the repo | |
is `private` and you do not have access. | |
- [`~utils.GatedRepoError`] | |
If the repository exists but is gated and the user is not on the authorized | |
list. | |
- [`~utils.RevisionNotFoundError`] | |
If the repository exists but the revision couldn't be find. | |
- [`~utils.EntryNotFoundError`] | |
If the repository exists but the entry (e.g. the requested file) couldn't be | |
find. | |
- [`~utils.BadRequestError`] | |
If request failed with a HTTP 400 BadRequest error. | |
- [`~utils.HfHubHTTPError`] | |
If request failed for a reason not listed above. | |
</Tip> | |
""" | |
try: | |
response.raise_for_status() | |
except HTTPError as e: | |
error_code = response.headers.get("X-Error-Code") | |
error_message = response.headers.get("X-Error-Message") | |
if error_code == "RevisionNotFound": | |
message = f"{response.status_code} Client Error." + "\n\n" + f"Revision Not Found for url: {response.url}." | |
raise RevisionNotFoundError(message, response) from e | |
elif error_code == "EntryNotFound": | |
message = f"{response.status_code} Client Error." + "\n\n" + f"Entry Not Found for url: {response.url}." | |
raise EntryNotFoundError(message, response) from e | |
elif error_code == "GatedRepo": | |
message = ( | |
f"{response.status_code} Client Error." + "\n\n" + f"Cannot access gated repo for url {response.url}." | |
) | |
raise GatedRepoError(message, response) from e | |
elif error_message == "Access to this resource is disabled.": | |
message = ( | |
f"{response.status_code} Client Error." | |
+ "\n\n" | |
+ f"Cannot access repository for url {response.url}." | |
+ "\n" | |
+ "Access to this resource is disabled." | |
) | |
raise DisabledRepoError(message, response) from e | |
elif error_code == "RepoNotFound" or ( | |
response.status_code == 401 | |
and response.request is not None | |
and response.request.url is not None | |
and REPO_API_REGEX.search(response.request.url) is not None | |
): | |
# 401 is misleading as it is returned for: | |
# - private and gated repos if user is not authenticated | |
# - missing repos | |
# => for now, we process them as `RepoNotFound` anyway. | |
# See https://gist.github.com/Wauplin/46c27ad266b15998ce56a6603796f0b9 | |
message = ( | |
f"{response.status_code} Client Error." | |
+ "\n\n" | |
+ f"Repository Not Found for url: {response.url}." | |
+ "\nPlease make sure you specified the correct `repo_id` and" | |
" `repo_type`.\nIf you are trying to access a private or gated repo," | |
" make sure you are authenticated." | |
) | |
raise RepositoryNotFoundError(message, response) from e | |
elif response.status_code == 400: | |
message = ( | |
f"\n\nBad request for {endpoint_name} endpoint:" if endpoint_name is not None else "\n\nBad request:" | |
) | |
raise BadRequestError(message, response=response) from e | |
elif response.status_code == 403: | |
message = ( | |
f"\n\n{response.status_code} Forbidden: {error_message}." | |
+ f"\nCannot access content at: {response.url}." | |
+ "\nIf you are trying to create or update content," | |
+ "make sure you have a token with the `write` role." | |
) | |
raise HfHubHTTPError(message, response=response) from e | |
# Convert `HTTPError` into a `HfHubHTTPError` to display request information | |
# as well (request id and/or server error message) | |
raise HfHubHTTPError(str(e), response=response) from e | |
def _format_error_message(message: str, request_id: Optional[str], server_message: Optional[str]) -> str: | |
""" | |
Format the `HfHubHTTPError` error message based on initial message and information | |
returned by the server. | |
Used when initializing `HfHubHTTPError`. | |
""" | |
# Add message from response body | |
if server_message is not None and len(server_message) > 0 and server_message.lower() not in message.lower(): | |
if "\n\n" in message: | |
message += "\n" + server_message | |
else: | |
message += "\n\n" + server_message | |
# Add Request ID | |
if request_id is not None and str(request_id).lower() not in message.lower(): | |
request_id_message = f" (Request ID: {request_id})" | |
if "\n" in message: | |
newline_index = message.index("\n") | |
message = message[:newline_index] + request_id_message + message[newline_index:] | |
else: | |
message += request_id_message | |
return message | |