A one-flag kill-switch: when set, the pack installs no middleware, registers no refresh routes, computes no fingerprint and exposes no graph node — ComfyUI runs exactly as if it weren't installed. Only the read-only /tenaciousload/status route stays so the loading-screen overlay still shows (a generic 'Loading node definitions…' bar, since there's no build to track). The refresh menu buttons hide themselves when status reports enabled:false. Useful for A/B testing or as a safety kill-switch. Requires a restart (the middleware is installed at startup). Unit-tested both modes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ComfyUI-Tenaciousload
Self-contained fix for slow / black-screen ComfyUI loading when you have a huge model/LoRA collection (especially on a network mount). Just install the pack and restart ComfyUI — no nginx, no docker, no extra port.
The problem
ComfyUI's /api/object_info enumerates every node's inputs. With thousands of
LoRAs (worse on a network mount) it becomes tens of MB and takes minutes to
build on every page load — and the build freezes ComfyUI's whole event
loop, so you get a long black screen, worst over a remote network.
How this pack fixes it
On load it injects an aiohttp middleware into ComfyUI that intercepts
/object_info and /api/object_info and:
- caches the built response in memory and on disk (
./cache/), so it is built once instead of on every load — and the disk copy makes restarts instant (no rebuild); - serves it gzipped (≈85% smaller transfer, independent of any CLI flag), straight from cache without running the build;
- because the build never runs on a normal load, the event-loop freeze (and the long black screen) is gone — page loads drop from minutes to seconds.
The only time a build runs is the first load after install, or when you explicitly refresh (below).
Refreshing after you add / remove models or LoRAs
The cache holds the old model lists until you refresh. Three modes are available
from the Extensions menu (and the command palette):
| Mode | What it does | Speed |
|---|---|---|
| ⚡ Quick refresh | Re-walks only the folders whose timestamp changed since the last scan; reuses the cache for the rest. Catches new / removed / renamed files. | Fast on local disks; ~2× faster on a slow network mount (it still has to stat every folder to find which changed). |
| 🔄 Full refresh | Clears ComfyUI's folder cache and re-walks everything, ignoring timestamps. Catches moves/deletes anywhere. Use this for files you just added. | Slowest (the original behaviour). |
| ➕ Register new file… | You give it the path(s) of the file(s) you just added; it appends them to the cache with no folder walk. | Instant disk-wise — only the object_info rebuild remains. |
Also available:
- Graph node
🔄 Refresh Models/LoRAs (Tenaciousload)with amodewidget (quick/full), for automated workflows. - HTTP:
POST /tenaciousload/refreshwith{"mode": "quick" | "full" | "register", "folder": "loras", "files": ["pack/new.safetensors"]}, thenGET /object_info?nocache=1.
The first Quick refresh after install builds a folder index (one full walk), so it's as slow as a Full refresh that one time; every Quick refresh after that is incremental. The index is saved to
./cache/scan_snapshot.json.
Network mounts (CIFS/SMB/NFS): Quick refresh detects changes by directory timestamp, which network filesystems can report with a delay or coarse resolution (e.g. a
cache=looseCIFS mount), so it may occasionally miss a brand-new file. If a just-added model doesn't show up after a Quick refresh, use Full refresh — it re-walks everything and doesn't rely on timestamps.
Whichever mode you pick, the button shows a "refreshing…" toast and normal loads stay instant.
Requirements
None to install. Only ComfyUI itself (tested on 0.23.0) and Python ≥ 3.8.
Everything used is Python stdlib or already bundled with ComfyUI (aiohttp,
folder_paths, server). The web button needs no npm packages.
Install
Clone (or copy) this repo into your ComfyUI custom_nodes/ folder and restart
ComfyUI:
cd ComfyUI/custom_nodes
git clone https://github.com/ethanfel/ComfyUI-Tenaciousload.git
# then restart ComfyUI
Nothing to pip install. ComfyUI-Manager can also install it from the registry.
Verify it's working
After restart, load the page once (first time builds + caches), then:
curl -s -H 'Accept-Encoding: gzip' -o /dev/null \
-w '%{time_total}s | %{size_download} bytes | %header{x-tenaciousload-cache} | %header{content-encoding}\n' \
http://127.0.0.1:8188/api/object_info # use your ComfyUI port
# expect after the first load: ~0.00Xs | ~10 MB | HIT | gzip
ComfyUI's startup log should show Tenaciousload: object_info cache middleware installed.
Recommended: gzip the rest of ComfyUI
This pack already gzips the cached object_info on its own. To also gzip
everything else ComfyUI serves — most importantly the hundreds of frontend
extension scripts, plus the other API responses — launch ComfyUI with its
built-in compression flag:
python main.py --listen --port 8188 --enable-compress-response-body
- It's a stock ComfyUI option (defined in
comfy/cli_args.py), not part of this pack, and it's optional — Tenaciousload works fine without it. - It's strongly recommended for remote access: those extension scripts are
many small requests that compress very well, so the flag noticeably cuts the
total transfer on top of the
object_infocache. - It costs a little CPU per response to compress; on a fast machine this is negligible compared to the bytes saved over the network.
Notes
- Loading status: instead of ComfyUI's silent "Comfy" splash, a small status
line shows whether it's serving from cache or building (with node count +
elapsed time), so a long rebuild isn't a black screen with no feedback. It
removes itself once the app is ready. Status is also at
GET /tenaciousload/status. - The disk cache lives in
./cache/(git-ignored). Delete it, or use the refresh button, to force a rebuild. - An nginx reverse proxy can cache
object_infoat the HTTP layer too, but this pack does it in-process so no extra service, container, or port is needed. - New files in the
inputfolder are picked up by a refresh button by default (same as new models) — they do not trigger an automatic rebuild on restart. To auto-detect them on restart instead, setTENACIOUSLOAD_WATCH_INPUT=1. Only do this if your input folder is fairly static: on a busy input folder (e.g. a video workflow that adds clips constantly) the input dir changes on nearly every restart, which would invalidate the cache and force a slow rebuild each time — defeating the point.
Installing / updating other custom nodes
This pack is a quiet neighbour:
- No dependencies — its
requirements.txtis empty, so it can't cause the pip version conflicts that break other nodes' installs/updates. It also never touches other nodes' files or the ComfyUI-Manager installer. - Auto-detects node changes — it fingerprints the installed node set
(
NODE_CLASS_MAPPINGS) and, on the first page load after a restart, drops the cache automatically if a node was installed, updated, enabled or removed — so new nodes appear with no manual refresh. - The only thing it gates is the full
/object_infolist (a cached snapshot); it passes every other request straight through, so other nodes' own routes, sidebars and refresh buttons are unaffected. For an in-place node tweak that changes an existing node's inputs without adding/removing a node class, use a refresh button.
Disabling (keep only the loading bar)
Set TENACIOUSLOAD_DISABLED=1 (and restart) to turn the pack into a no-op:
no caching middleware, no fingerprint, no refresh routes, no graph node — ComfyUI
behaves exactly as if the pack weren't installed. The loading-screen status
bar stays (it just shows a generic "Loading node definitions…" since there's no
build to track). Useful for A/B comparing, or as a one-flag kill-switch if you
ever suspect the cache. Remove the variable (or set it to 0) and restart to
re-enable.
License
MIT — see LICENSE.