Skip to content

Fix stale checkpoint cleanup in save_pretrained - #14774

Open
kyo-zzz wants to merge 1 commit into
huggingface:mainfrom
kyo-zzz:fix/save-pretrained-stale-checkpoints
Open

kyo-zzz wants to merge 1 commit into
huggingface:mainfrom
kyo-zzz:fix/save-pretrained-stale-checkpoints

Conversation

@kyo-zzz

@kyo-zzz kyo-zzz commented Sep 15, 2026

Copy link
Copy Markdown

Fixes #14769.

ModelMixin.save_pretrained cleans a previous save by removing files that match the shard pattern of the current save, but it only recognized sharded files. Single-file checkpoints of the other container were left behind: re-saving a .safetensors checkpoint as .bin (or the reverse) kept the old file on disk, and because from_pretrained prefers sharded and safetensors checkpoints, the stale one silently took precedence over the freshly written weights — no error, no warning.

Changes

  • Extract the cleanup into _get_superseded_checkpoint_files, which returns every artifact of the current save's variant that the new save replaces: single-file weights, shards, and sharded indexes, in either container.
  • Scope is the variant stem (diffusion_pytorch_model vs diffusion_pytorch_model.ema), so checkpoints of other variants that share the directory are preserved.
  • Sharded indexes are now cleaned as well, which fixes the stale-index FileNotFoundError case from the issue matrix.

Tests

tests/models/test_modeling_common.py::TestModelUtils

  • test_save_pretrained_removes_superseded_checkpoints[None|ema] — parametrized over both variants and covers the container switch in both directions; asserts the previous file is gone and that reloaded weights match the freshly written ones.
  • test_save_pretrained_preserves_other_variants — a plain save and a variant="ema" save coexist in the same directory.

Verified red-then-green: reverting the fix makes both parametrized cases fail with the stale .safetensors still present (assert 'diffusion_pytorch_model.ema.safetensors' not in [...]), and they pass again with the fix applied.

`ModelMixin.save_pretrained` cleaned a previous save by removing files whose
name matched the shard pattern of the current save, but it only recognized
sharded files. Single-file checkpoints of the other container were left
behind: re-saving a `.safetensors` checkpoint as `.bin` (or the reverse) kept
the old file on disk, and because `from_pretrained` prefers sharded and
safetensors checkpoints, the stale one silently took precedence over the
freshly written weights.

Extract the cleanup into `_get_superseded_checkpoint_files`, which returns
every artifact of the current save's variant that the new save replaces:
single-file weights, shards, and sharded indexes, in either container. Files
belonging to other variants (e.g. `ema`) share the directory and are left
untouched.

Add a regression test covering both directions of the container switch and
the variant coexistence case.

Fixes huggingface#14769
@github-actions github-actions Bot added fixes-issue models tests size/M PR with diff < 200 LOC and removed fixes-issue labels Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

models size/M PR with diff < 200 LOC tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

save_pretrained(safe_serialization=False) leaves the old safetensors checkpoint behind, and from_pretrained loads it instead of the new weights

1 participant