Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define error #2444

Merged
merged 8 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/spacy/test_spacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from spacy_huggingface_hub import push

from huggingface_hub import delete_repo, hf_hub_download, model_info
from huggingface_hub.utils import HfHubHTTPError
from huggingface_hub.errors import HfHubHTTPError

from ..utils import production_endpoint

Expand Down
2 changes: 1 addition & 1 deletion src/huggingface_hub/_commit_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
from tqdm.contrib.concurrent import thread_map

from .constants import ENDPOINT, HF_HUB_ENABLE_HF_TRANSFER
from .errors import EntryNotFoundError
from .file_download import hf_hub_url
from .lfs import UploadInfo, lfs_upload, post_lfs_batch_info
from .utils import (
FORBIDDEN_FOLDERS,
EntryNotFoundError,
chunk_iterable,
get_session,
hf_raise_for_status,
Expand Down
5 changes: 1 addition & 4 deletions src/huggingface_hub/_snapshot_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,11 @@
HF_HUB_ENABLE_HF_TRANSFER,
REPO_TYPES,
)
from .errors import GatedRepoError, LocalEntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError
from .file_download import REGEX_COMMIT_HASH, hf_hub_download, repo_folder_name
from .hf_api import DatasetInfo, HfApi, ModelInfo, SpaceInfo
from .utils import (
GatedRepoError,
LocalEntryNotFoundError,
OfflineModeIsEnabled,
RepositoryNotFoundError,
RevisionNotFoundError,
filter_repo_objects,
logging,
validate_hf_hub_args,
Expand Down
3 changes: 2 additions & 1 deletion src/huggingface_hub/_tensorboard_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
from typing import TYPE_CHECKING, List, Optional, Union

from ._commit_scheduler import CommitScheduler
from .errors import EntryNotFoundError
from .repocard import ModelCard
from .utils import EntryNotFoundError, experimental
from .utils import experimental


# Depending on user's setup, SummaryWriter can come either from 'tensorboardX'
Expand Down
2 changes: 1 addition & 1 deletion src/huggingface_hub/commands/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
)
from huggingface_hub.hf_api import HfApi

from ..utils import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError
from ..errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError
from ._cli_utils import ANSI


Expand Down
3 changes: 2 additions & 1 deletion src/huggingface_hub/commands/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@
from huggingface_hub._commit_scheduler import CommitScheduler
from huggingface_hub.commands import BaseHuggingfaceCLICommand
from huggingface_hub.constants import HF_HUB_ENABLE_HF_TRANSFER
from huggingface_hub.errors import RevisionNotFoundError
from huggingface_hub.hf_api import HfApi
from huggingface_hub.utils import RevisionNotFoundError, disable_progress_bars, enable_progress_bars
from huggingface_hub.utils import disable_progress_bars, enable_progress_bars


logger = logging.get_logger(__name__)
Expand Down
266 changes: 265 additions & 1 deletion src/huggingface_hub/errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"""Contains all custom errors."""

from requests import HTTPError
from typing import Optional

from requests import HTTPError, Response

from .utils._fixes import JSONDecodeError


# HEADERS ERRORS
Expand All @@ -17,6 +21,88 @@ class OfflineModeIsEnabled(ConnectionError):
"""Raised when a request is made but `HF_HUB_OFFLINE=1` is set as environment variable."""


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:]


# INFERENCE CLIENT ERRORS


Expand Down Expand Up @@ -94,3 +180,181 @@ class HFValidationError(ValueError):

Inherits from [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError).
"""


# FILE METADATA ERRORS


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.
"""


# REPOSIORY ERRORS


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.
```
"""


# REVISION ERROR


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.
```
"""


# ENTRY ERRORS
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)


# REQUEST ERROR
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 _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
6 changes: 4 additions & 2 deletions src/huggingface_hub/file_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@
REPO_TYPES,
REPO_TYPES_URL_PREFIXES,
)
from .utils import (
from .errors import (
EntryNotFoundError,
FileMetadataError,
GatedRepoError,
LocalEntryNotFoundError,
OfflineModeIsEnabled,
RepositoryNotFoundError,
RevisionNotFoundError,
)
from .utils import (
OfflineModeIsEnabled,
SoftTemporaryDirectory,
WeakFileLock,
build_hf_headers,
Expand Down
14 changes: 8 additions & 6 deletions src/huggingface_hub/hf_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,21 @@
DiscussionStatusFilter,
DiscussionTypeFilter,
)
from .errors import (
BadRequestError,
EntryNotFoundError,
GatedRepoError,
HfHubHTTPError,
RepositoryNotFoundError,
RevisionNotFoundError,
)
from .file_download import HfFileMetadata, get_hf_file_metadata, hf_hub_url
from .repocard_data import DatasetCardData, ModelCardData, SpaceCardData
from .utils import (
DEFAULT_IGNORE_PATTERNS,
BadRequestError,
EntryNotFoundError,
GatedRepoError,
HfFolder, # noqa: F401 # kept for backward compatibility
HfHubHTTPError,
LocalTokenNotFoundError,
NotASafetensorsRepoError,
RepositoryNotFoundError,
RevisionNotFoundError,
SafetensorsFileMetadata,
SafetensorsParsingError,
SafetensorsRepoMetadata,
Expand Down
Loading
Loading