Skip to content

align snapshot_download to respect hfh latest version#14118

Open
sayakpaul wants to merge 7 commits into
mainfrom
hfh-snapshot-download-fix
Open

align snapshot_download to respect hfh latest version#14118
sayakpaul wants to merge 7 commits into
mainfrom
hfh-snapshot-download-fix

Conversation

@sayakpaul

Copy link
Copy Markdown
Member

What does this PR do?

Fixes #14117

@BenjaminBossan does this work for you?

@BenjaminBossan

Copy link
Copy Markdown
Member

The PEFT tests succeed on this branch, thanks a lot.

(reminder to myself: testing with pytest "tests/test_stablediffusion.py::TestStableDiffusionModel::test_disable_adapter" "tests/test_tuners_utils.py::TestPeftCustomKwargs::test_maybe_include_all_linear_layers_diffusion" -v)

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@sayakpaul sayakpaul requested a review from BenjaminBossan July 3, 2026 16:07
@sayakpaul sayakpaul marked this pull request as draft July 3, 2026 16:25

@BenjaminBossan BenjaminBossan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment on the logic of the fix, but it resolves the PEFT errors. Thanks.

lambda image_url_or_path: load_image(image_url_or_path)
if urlparse(image_url_or_path).scheme
else Image.open(image_url_or_path).convert("RGB")
lambda image_url_or_path: (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes seem to be unrelated.

@BenjaminBossan

Copy link
Copy Markdown
Member

A question: Is there going to be a patch release with this fix? If not, I need to prepare a fix on PEFT to exempt the failing tests until Diffusers 0.40 is released.

@sayakpaul

Copy link
Copy Markdown
Member Author

It's a breaking change from hfh and not diffusers. So, maybe you will need to wait till the next release.

BenjaminBossan added a commit to huggingface/peft that referenced this pull request Jul 6, 2026
Due to a recent change in huggingface-hub, loading Diffusers pipelines
in offline mode can fail. Since the PEFT caching mechanism works by
setting offline mode, several PEFT tests are affected by this.

There is a fix on the way in Diffusers, but there won't be a patch
release:

huggingface/diffusers#14118

Therefore, we skip these tests for now. Once Diffusers 0.40.0
releases, the tests will run again, but we can also remove the
skipping logic completely.

@Wauplin Wauplin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR feels correct 👍 (sorry for the delay reviewing it)

Though for a broader context I still do not understand why the allow_pattern/ignore_pattern arguments are different depending on the Hub availability

It's a breaking change from hfh and not diffusers. So, maybe you will need to wait till the next release.

It's not considered as a breaking change on hfh-side so it won't be fixed in next releases no

Comment on lines +1793 to +1801
config_file = hf_hub_download(
pretrained_model_name,
cls.config_name,
cache_dir=cache_dir,
revision=revision,
token=token,
local_files_only=True,
)
return Path(config_file).parent

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hacky but "works" though you don't benefit from the new "raise if incomplete snapshot"-feature i.e. if a previous download failed mid-way it won't be caught here, meaning a separate error will be raised downstream (likely when trying to load the model) - and the base exception will have been lost in the process

@Wauplin Wauplin self-requested a review July 7, 2026 14:51

@Wauplin Wauplin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(sorry just wanted to "comment" instead of "approve" -not well versed with diffusers internals- but the summary is the same^^)

@BenjaminBossan

Copy link
Copy Markdown
Member

It's a breaking change from hfh and not diffusers. So, maybe you will need to wait till the next release.

It's not considered as a breaking change on hfh-side so it won't be fixed in next releases no

I took this to mean that it will have to wait until the next Diffusers (minor) release, which will contain this fix, as there won't be a Diffusers patch release for this.

@sayakpaul

Copy link
Copy Markdown
Member Author

It's not considered as a breaking change on hfh-side so it won't be fixed in next releases no

Next release of Diffusers. I am not sure why it's not considered a breaking change on HFH side because it clearly broke things in Diffusers as we can see. From what I understand, the behaviour of the underlying HFH-side functions also changed. Maybe I am missing something.

@Wauplin

Wauplin commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

. I am not sure why it's not considered a breaking change on HFH side because it clearly broke things in Diffusers as we can see.

Mostly because it's considered as bug fix (I know it's mostly semantic^^)

because it clearly broke things in Diffusers as we can see

did it broke things only in the tests or in real life? I feel like if we are now raising an error for an incomplete download it's better than having diffusers raise because a file is not found in a local snapshot that was supposed to contain it

Instead of returning the cached snapshot folder without validation when
offline, compute the same allow/ignore patterns as the online path
(sourcing the file listing from the cached snapshot instead of
model_info) and let snapshot_download validate the cached snapshot
against them, so an interrupted download surfaces hfh's
IncompleteSnapshotError instead of failing later at model load time.
Also revert the _get_checkpoint_shard_files divergence: its patterns
were already identical in both modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sayakpaul sayakpaul marked this pull request as ready for review July 7, 2026 16:21
@sayakpaul sayakpaul requested a review from Wauplin July 7, 2026 16:27
huggingface_hub>=1.22.0 reports a missing cached shard via
IncompleteSnapshotError (repo-relative path) before diffusers' own
cached-folder check (absolute path) runs, so assert on the shard's
filename, which both messages contain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment on lines +1674 to +1691
try:
config_file = hf_hub_download(
pretrained_model_name,
cls.config_name,
cache_dir=cache_dir,
revision=revision,
token=token,
local_files_only=True,
)
except LocalEntryNotFoundError:
config_file = None

if config_file is not None:
snapshot_folder = Path(config_file).parent
if local_files_only:
filenames = {
f.relative_to(snapshot_folder).as_posix() for f in snapshot_folder.rglob("*") if f.is_file()
}

@Wauplin Wauplin Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requesting to download a single file to then use Path(...).parent is IMO a hacky way of getting the snapshot_folder. (hence why it needs 7 lines of explanation in comments)

to avoid that I've opened huggingface/huggingface_hub#4500 to expose the snapshot_folder in the error in case of IncompleteSnapshotError. I think this is the most explicit way to do things. In diffusers the code will look like this:

try:
    snapshot_path = snapshot_download(...)
except IncompleteSnapshotError as error:
    incomplete_snapshot_path = error.snapshot_path

making it explicit for anyone reading the code that the folder is incomplete and that we knowingly ignored it

(even though, once again, I do think the best way to tackle this is to provide the correct allow_pattern/ignore_pattern filters in the first place. I still don't understand why it's not the solution we are trying to have in this PR. If there is a rational behind not trying to fix the root cause I genuinely believe it should at the very least be mentioned/documented somewhere)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for offering further thoughts. Post-mortem:

The reason we can't write the patterns down without any file listing: they're data-dependent. allow_patterns are largely concrete filenames chosen by variant_compatible_siblings (variant / sharded / safetensors-vs-bin resolution), and _get_ignore_patterns picks formats based on what actually exists in the repo.

For the online path, that listing comes from model_info().siblings. For the offline path, the only faithful source is the cached snapshot itself, which holds exactly what the same computation downloaded when online — so both modes compute the same patterns. We also test it here:

def test_local_files_only_uses_same_snapshot_download_patterns(self):

More precisely, the offline path falls through into the same pattern-computation code as online, fed with a filenames' set that came from the cache instead of model_info().siblings. Before this PR, the offline path skipped all of this (patterns stayed None).

Regarding hf_hub_download(config_file), the online branch makes the identical call because model_index.json's content dictates the computation (component folders, _ignore_files, custom pipeline):

if not local_files_only:
config_file = hf_hub_download(
pretrained_model_name,
cls.config_name,
cache_dir=cache_dir,
revision=revision,
proxies=proxies,
force_download=force_download,
token=token,
)

(All of that is used for computing the patterns)

For the offline path, we are also reusing its parent as the snapshot dir, matching the pre-existing snapshot_folder = Path(config_file).parent on main.

huggingface/huggingface_hub#4500 is cool, thanks! snapshot_path on the error does locate the folder correctly. But the error only fires from snapshot_download.We need the folder before calling snapshot_download (its listing is the input to the pattern computation). Using it would mean a deliberate no-patterns call that fails on every offline diffusers load just to catch the error.

Maybe there's a way to refactor this but that's for another PR.

@Wauplin Wauplin Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed response! That makes sense then. So if I understand correctly what's missing before-hand is the list of files in the repo when in offline mode, right? Luckily we now have this information in cache -hence why we are able to tell if a folder is incomplete-. We could definitely add a method to retrieve that cached list to that you can:

  1. make a repo info call to get repo.siblings
  2. if that fails, get repo.siblings from the cache (requires a method from huggingface_hub)
  3. and then compute the allow/ignore patterns from the list of files, no matter if you are in online or offline mode

Would that be interesting for you?

Using it would mean a deliberate no-patterns call that fails on every offline diffusers load just to catch the error.

Yes that's what I had in mind. I feel that it's less hacky than a deliberate hf_hub_download call on the index file, just to do a Path(index file).parent afterwards.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opened huggingface/huggingface_hub#4513 to add a get_cached_repo_tree method useful when repo.siblings or list_repo_tree fails (in offline mode). A priori, this is the cleanest way to determine the patterns down the line (with variants, etc.) without raising an error and without skipping the "incomplete folder check". Should be shipped today or tomorrow with the rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DiffusionPipeline.download() breaks with huggingface_hub>=1.22.0 in offline mode (IncompleteSnapshotError)

4 participants