fix: detect missing checkpoint file and surface clear error
User report on a different machine: 'AttributeError: NoneType has no attribute
lower' at comfy/utils.py:126 inside load_torch_file. Root cause: manifest's
meta.model references a checkpoint that isn't installed on this machine —
folder_paths.get_full_path returns None, that None propagates through
load_checkpoint_guess_config to load_torch_file, crashes cryptically.
Two defensive checks:
1. upscale_runner._run_upscale_thread: validates the session checkpoint
resolves to a real file BEFORE calling load_checkpoint. If not, sets
job['error'] with a clear actionable message ('Copy the .safetensors
file over, or regenerate the images on this machine') and exits early.
2. florence2_hires._get_or_load_checkpoint_lora: same check before the
fresh-load path (different per-item model). On missing file, falls back
to the session base with a one-line log so the upscale still completes
instead of crashing the whole job.
Tests: added folder_paths.get_full_path to the root conftest stub returning
a synthetic path so mocked-loader tests don't trip the new file-check.
96/96 still passing.
This commit is contained in:
@@ -58,6 +58,12 @@ def _install_stubs():
|
||||
fp = sys.modules["folder_paths"]
|
||||
if not hasattr(fp, "get_output_directory"):
|
||||
fp.get_output_directory = lambda: os.path.join(_NODE_ROOT, "_test_output") # type: ignore
|
||||
if not hasattr(fp, "get_full_path"):
|
||||
# Stub returns a synthetic path for any (folder, name) lookup so tests
|
||||
# that don't care about file existence (e.g. mocked loaders) don't trip
|
||||
# the "file not found -> fall back" guard in florence2_hires. Tests that
|
||||
# specifically want to exercise the missing-file path can monkeypatch.
|
||||
fp.get_full_path = lambda folder, name: f"<stubbed>/{folder}/{name}" if name else None # type: ignore
|
||||
|
||||
# server.PromptServer
|
||||
if "server" not in sys.modules:
|
||||
|
||||
+19
-1
@@ -401,7 +401,25 @@ def _get_or_load_checkpoint_lora(item, fallback, model_source, cache, session_mo
|
||||
cache[key] = result
|
||||
return result
|
||||
|
||||
# Different model — full load.
|
||||
# Different model — full load. Validate the file exists first; without this
|
||||
# guard a missing checkpoint hits comfy/utils.load_torch_file(None,...) and
|
||||
# crashes with "'NoneType' has no attribute 'lower'". Common scenario: manifest
|
||||
# was generated on a different machine that had the per-item checkpoint
|
||||
# installed.
|
||||
try:
|
||||
import folder_paths as _fp_check
|
||||
ckpt_path_check = _fp_check.get_full_path("checkpoints", model_name)
|
||||
except Exception:
|
||||
ckpt_path_check = None
|
||||
if ckpt_path_check is None:
|
||||
print(
|
||||
f"[Florence2HiResFix] Item references model '{model_name}' but the "
|
||||
f"file is not installed in ComfyUI/models/checkpoints/. Falling back to "
|
||||
f"session default."
|
||||
)
|
||||
cache[key] = fallback
|
||||
return fallback
|
||||
|
||||
try:
|
||||
model, clip, vae = ckpt_fn(
|
||||
target_model_name=model_name,
|
||||
|
||||
+22
-1
@@ -124,9 +124,30 @@ def _run_upscale_thread(job, target_items, upscale_config, meta, manifest_data,
|
||||
_ext = "jpg" if _fmt in ("jpg", "jpeg") else _fmt
|
||||
|
||||
# Load model, VAE, CLIP for conditioning
|
||||
model_name = meta.get("model", "")
|
||||
model_name = meta.get("model", "") or ""
|
||||
vae_name = meta.get("vae", "")
|
||||
|
||||
# Validate the checkpoint exists before calling load_checkpoint. Without
|
||||
# this guard, a missing file ends up at comfy/utils.py:load_torch_file(None,
|
||||
# ...) and crashes with the cryptic "'NoneType' object has no attribute
|
||||
# 'lower'". Common cause: manifest copied from another machine where the
|
||||
# checkpoint isn't installed; or manifest entry has model=None.
|
||||
try:
|
||||
import folder_paths as _fp_check
|
||||
ckpt_path_check = _fp_check.get_full_path("checkpoints", model_name) if model_name else None
|
||||
except Exception:
|
||||
ckpt_path_check = None
|
||||
if not model_name or ckpt_path_check is None:
|
||||
job["status"] = "error"
|
||||
job["error"] = (
|
||||
f"Checkpoint '{model_name or '(empty)'}' not found in "
|
||||
f"ComfyUI/models/checkpoints/. The session manifest references it but "
|
||||
f"the file is not installed on this machine. Copy the .safetensors "
|
||||
f"file over, or regenerate the images on this machine first."
|
||||
)
|
||||
print(f"[DashboardUpscale] ❌ {job['error']}")
|
||||
return
|
||||
|
||||
# VRAM hygiene before load_checkpoint: the previous workflow's model handles
|
||||
# may still be Python-referenced and won't be evicted by ComfyUI's LRU on a
|
||||
# new load_checkpoint call. soft_empty_cache + gc.collect releases those
|
||||
|
||||
Reference in New Issue
Block a user