Fix safetensors validation to catch corruption after download (#14465)

This commit is contained in:
Alison Shao
2025-12-05 16:04:00 -08:00
committed by GitHub
parent e41664ba1a
commit b988c18eae
3 changed files with 70 additions and 10 deletions

View File

@@ -47,7 +47,6 @@ from sglang.srt.model_loader.weight_validation import (
_validate_sharded_model,
)
from sglang.srt.utils import find_local_repo_dir, log_info_on_rank0, print_warning_once
from sglang.utils import is_in_ci
logger = logging.getLogger(__name__)
@@ -436,6 +435,55 @@ def find_local_hf_snapshot_dir(
return None
def _validate_weights_after_download(
hf_folder: str,
allow_patterns: List[str],
model_name_or_path: str,
) -> None:
"""Validate downloaded weight files to catch corruption early.
This function validates safetensors files after download to catch
corruption issues (truncated downloads, network errors, etc.) before
model loading fails with cryptic errors.
Args:
hf_folder: Path to the downloaded model folder
allow_patterns: Patterns used to match weight files
model_name_or_path: Model identifier for error messages
Raises:
RuntimeError: If any weight files are corrupted
"""
import glob as glob_module
# Find all weight files that were downloaded
weight_files: List[str] = []
for pattern in allow_patterns:
weight_files.extend(glob_module.glob(os.path.join(hf_folder, pattern)))
if not weight_files:
return # No weight files to validate
# Validate safetensors files
corrupted_files = []
for f in weight_files:
if f.endswith(".safetensors") and os.path.exists(f):
if not _validate_safetensors_file(f):
corrupted_files.append(os.path.basename(f))
if corrupted_files:
# Clean up corrupted files so next attempt re-downloads them
_cleanup_corrupted_files_selective(
model_name_or_path,
[os.path.join(hf_folder, f) for f in corrupted_files],
)
raise RuntimeError(
f"Downloaded model files are corrupted for {model_name_or_path}: "
f"{corrupted_files}. The corrupted files have been removed. "
"Please retry to re-download the model."
)
def download_weights_from_hf(
model_name_or_path: str,
cache_dir: Optional[str],
@@ -461,17 +509,19 @@ def download_weights_from_hf(
str: The path to the downloaded model weights.
"""
if is_in_ci():
# If the weights are already local, skip downloading and returns the path.
# This is used to skip too-many Huggingface API calls in CI.
path = find_local_hf_snapshot_dir(
model_name_or_path, cache_dir, allow_patterns, revision
)
if path is not None:
return path
# Always check for valid local cache first.
# This validates cached files and cleans up corrupted ones.
path = find_local_hf_snapshot_dir(
model_name_or_path, cache_dir, allow_patterns, revision
)
if path is not None:
# Valid local cache found, skip download
return path
# In CI, skip HF API calls if we're in offline mode or want to avoid rate limits
# But we already checked for local cache above, so if we're here we need to download
if not huggingface_hub.constants.HF_HUB_OFFLINE:
# Before we download we look at that is available:
# Before we download we look at what is available:
fs = HfFileSystem()
file_list = fs.ls(model_name_or_path, detail=False, revision=revision)
@@ -495,6 +545,10 @@ def download_weights_from_hf(
revision=revision,
local_files_only=huggingface_hub.constants.HF_HUB_OFFLINE,
)
# Validate downloaded files to catch corruption early
_validate_weights_after_download(hf_folder, allow_patterns, model_name_or_path)
return hf_folder