Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion folder_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,12 @@ def cached_filename_list_(folder_name: str) -> tuple[list[str], dict[str, float]
for x in out[1]:
time_modified = out[1][x]
folder = x
if os.path.getmtime(folder) != time_modified:
try:
if os.path.getmtime(folder) != time_modified:
return None
except OSError:
# A tracked folder was deleted or became inaccessible; treat the
# cache as stale so it gets rebuilt instead of raising.
return None
Comment on lines +415 to 421

folders = folder_names_and_paths[folder_name]
Expand Down
46 changes: 46 additions & 0 deletions tests-unit/folder_paths_test/cache_invalidation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import shutil
import tempfile

import pytest

import folder_paths


@pytest.fixture
def model_folder():
"""Register a temporary model category with a tracked subfolder."""
folder_name = "cache_invalidation_test_cat"
with tempfile.TemporaryDirectory() as base:
category = os.path.join(base, "category")
subfolder = os.path.join(category, "sub")
os.makedirs(subfolder)
open(os.path.join(category, "a.safetensors"), "w").close()
open(os.path.join(subfolder, "b.safetensors"), "w").close()

folder_paths.folder_names_and_paths[folder_name] = (
[category],
{".safetensors"},
)
try:
yield folder_name, category, subfolder
finally:
folder_paths.folder_names_and_paths.pop(folder_name, None)
folder_paths.filename_list_cache.pop(folder_name, None)
folder_paths.cache_helper.clear()
Comment on lines +28 to +30


def test_rebuilds_when_tracked_subfolder_deleted(model_folder):
folder_name, _category, subfolder = model_folder

# Populate the filename cache, which records the mtime of every subfolder.
initial = folder_paths.get_filename_list(folder_name)
assert sorted(initial) == ["a.safetensors", "sub/b.safetensors"]

# Remove a tracked subfolder at runtime (e.g. user deletes a model folder).
shutil.rmtree(subfolder)

# The cache must be treated as stale and rebuilt, not crash with
# FileNotFoundError when probing the deleted folder's mtime.
refreshed = folder_paths.get_filename_list(folder_name)
assert refreshed == ["a.safetensors"]
Comment on lines +45 to +46