_errors.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. # Copyright 2026 The HuggingFace Team. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """CLI error handling utilities."""
  15. import traceback
  16. from collections.abc import Callable
  17. from huggingface_hub.errors import (
  18. BucketNotFoundError,
  19. CLIError,
  20. CLIExtensionInstallError,
  21. GatedRepoError,
  22. HfHubHTTPError,
  23. LocalTokenNotFoundError,
  24. RemoteEntryNotFoundError,
  25. RepositoryNotFoundError,
  26. RevisionNotFoundError,
  27. )
  28. def _format_repo_not_found(error: RepositoryNotFoundError) -> str:
  29. label = error.repo_type.capitalize() if error.repo_type else "Repository"
  30. if error.repo_id:
  31. msg = f"{label} '{error.repo_id}' not found."
  32. else:
  33. msg = f"{label} not found."
  34. msg += " If the repo is private, make sure you are authenticated and your token has the required permissions."
  35. return msg
  36. def _format_gated_repo(error: GatedRepoError) -> str:
  37. label = error.repo_type if error.repo_type else "repository"
  38. if error.repo_id:
  39. return f"Access denied. {label.capitalize()} '{error.repo_id}' requires approval."
  40. return f"Access denied. This {label} requires approval."
  41. def _format_bucket_not_found(error: BucketNotFoundError) -> str:
  42. if error.bucket_id:
  43. return f"Bucket '{error.bucket_id}' not found. If the bucket is private, make sure you are authenticated and your token has the required permissions."
  44. return "Bucket not found. Check the bucket id (namespace/name). If the bucket is private, make sure you are authenticated and your token has the required permissions."
  45. def _format_entry_not_found(error: RemoteEntryNotFoundError) -> str:
  46. label = error.repo_type if error.repo_type else "repository"
  47. url = str(error.response.url) if error.response else None
  48. if error.repo_id:
  49. msg = f"File not found in {label} '{error.repo_id}'."
  50. else:
  51. msg = f"File not found in {label}."
  52. if url:
  53. msg += f"\nURL: {url}"
  54. return msg
  55. def _format_revision_not_found(error: RevisionNotFoundError) -> str:
  56. label = error.repo_type if error.repo_type else "repository"
  57. if error.repo_id:
  58. return f"Revision not found in {label} '{error.repo_id}'."
  59. return f"Revision not found in {label}. Check the revision parameter."
  60. def _format_cli_error(error: CLIError) -> str:
  61. """No traceback, just the error message."""
  62. return str(error)
  63. def _format_cli_extension_install_error(error: CLIExtensionInstallError) -> str:
  64. """Format a CLI extension installation error.
  65. The error is likely to be a tricky subprocess error to investigate. In this specific case we want to format the
  66. traceback of the root cause while keeping the "nicely formatted" error message of the CLIExtensionInstallError
  67. as a 1-line message.
  68. """
  69. cause_tb = (
  70. "".join(traceback.format_exception(type(error.__cause__), error.__cause__, error.__cause__.__traceback__))
  71. if error.__cause__ is not None
  72. else ""
  73. )
  74. return f"{cause_tb}\n{error}"
  75. CLI_ERROR_MAPPINGS: dict[type[Exception], Callable[..., str]] = {
  76. # GatedRepoError must come before RepositoryNotFoundError (it's a subclass).
  77. GatedRepoError: _format_gated_repo,
  78. BucketNotFoundError: _format_bucket_not_found,
  79. RepositoryNotFoundError: _format_repo_not_found,
  80. RevisionNotFoundError: _format_revision_not_found,
  81. LocalTokenNotFoundError: lambda _: "Not logged in. Run 'hf auth login' first.",
  82. RemoteEntryNotFoundError: _format_entry_not_found,
  83. HfHubHTTPError: lambda error: str(error),
  84. ValueError: lambda error: f"Invalid value. {error}",
  85. CLIExtensionInstallError: _format_cli_extension_install_error,
  86. CLIError: _format_cli_error,
  87. }
  88. def format_known_exception(error: Exception) -> str | None:
  89. for exc_type, formatter in CLI_ERROR_MAPPINGS.items():
  90. if isinstance(error, exc_type):
  91. return formatter(error)
  92. return None