Author: Junjie

  • ComfyUI –vram-headroom vs –reserve-vram: Why Your VRAM Limit Stopped Working After the Dynamic VRAM Update

    If you updated ComfyUI in mid-2026 and suddenly found that your carefully tuned --reserve-vram 2 flag no longer keeps your GPU from maxing out, you are not alone. There is an open issue on the Comfy-Org tracker titled “--reserve-vram ignored” describing exactly this on an RTX 5090. The reason is not that the flag broke — it is that ComfyUI shipped a new memory subsystem and a new sibling flag, --vram-headroom, and the two now mean very different things.

    This article explains what each flag actually does under the Dynamic VRAM system, how to diagnose which one you need, and how to stop ComfyUI from eating the last few gigabytes your desktop or browser still needs. Everything below is drawn from the official startup-flags reference and the Comfy-Org GitHub tracker; I have not run synthetic benchmarks, and I will not invent any.

    What Changed: Dynamic VRAM Became the Default

    ComfyUI’s Dynamic VRAM optimization — described in GitHub discussion #12699 — is now enabled by default on NVIDIA hardware (and ROCm 7.14+). Its whole job is to load and offload weights on demand instead of keeping everything resident, which is why it “massively reduces RAM usage and generally speeds things up” in the maintainers’ words.

    The side effect is that a lot of older memory flags no longer behave the way they used to. The official startup-flags reference now says outright that --lowvram has “no effect when dynamic VRAM is enabled.” When you pair that with the older flags you relied on, the result is exactly the confusion people report after an update: a workflow that used to fit now blows past the limit, or a flag that used to reserve memory seems to do nothing.

    Two flags matter most for capping VRAM, and they are easy to mix up:

    • --reserve-vram GB — the older flag. Reserves VRAM for the operating system and other software.
    • --vram-headroom GB — the newer flag, added alongside the Dynamic VRAM rework (PR #14480, 2026-06-15). Keeps extra VRAM free above the default headroom.

    What –vram-headroom Actually Does

    According to the startup-flags documentation, --vram-headroom GB (default 0) is the “extra VRAM in GB for dynamic VRAM to keep free above the default headroom.” The crucial detail is the last clause: ComfyUI “tries to keep this much VRAM completely free and unused, even counting VRAM used by other apps.”

    That last point is what makes it different from --reserve-vram. --vram-headroom is designed for the scenario where ComfyUI is not the only thing on the card — you have a browser with hardware acceleration, a desktop compositor, OBS, or a second app that also needs VRAM. By setting, say, --vram-headroom 2, you tell the dynamic allocator to leave 2 GB genuinely free even after accounting for whatever other processes are already holding.

    You launch it the same way as any other startup flag:

    python main.py --vram-headroom 2

    Or, on Windows Portable, add it to your .bat launcher (for example run_nvidia_gpu.bat) the same way you would any other argument.

    Why –reserve-vram Seems Ignored Now

    This is where the frustration kicks in. In open issue #15666, the reporter ran an RTX 5090 (31.5 GB) with --reserve-vram 2 and expected VRAM to never exceed 29.5 GB. Instead they observed the card hit 31.0 GB during model load and initialization, then “settle” to roughly 23.3 GB only after the run completed. Their conclusion — that --reserve-vram is effectively ignored during model load and initialization, then enforced afterward — mirrors what the maintainers and other commenters have begun to confirm.

    One commenter on that thread summarizes the emerging picture well: --vram-headroom may actually allocate its headroom up front to avoid writing into it, which is why Windows can still report near-full VRAM even though ComfyUI is respecting the cap. Either way, the practical takeaway is the same — if your goal is to keep a hard slice of VRAM free during the worst-case moment (model load, when pressure is highest), --reserve-vram alone is no longer the reliable tool it used to be.

    This is a moving target. The bug report is still open and assigned, so behaviors you read about today may change in the next point release. Treat the distinction below as “what the flags are documented to mean now,” not a promise that the underlying allocator has stopped surprising people.

    How to Choose the Right Flag (and Verify It)

    Start by deciding what problem you are actually solving, because the two flags address different ones:

    • You want ComfyUI to leave room for other running apps (a browser, OBS, the desktop) → use --vram-headroom GB. This is the flag built for “I need 2 GB left over for the rest of my system.”
    • You want to reserve VRAM for the OS on a headless or mostly-dedicated box → --reserve-vram GB still has a role, but understand its enforcement may be loose during model load under Dynamic VRAM.
    • You need dynamic VRAM entirely out of the picture → --disable-dynamic-vram, which falls back to estimate-based loading. I covered the broken-workflow and TensorRT OOM aspect of this in a separate post on fixing Dynamic VRAM OOM and broken TensorRT nodes.

    Once you pick a flag, do not trust your memory — verify with a live tool. On Linux, nvidia-smi in a second terminal shows per-process VRAM; on Windows, Task Manager’s GPU view or nvidia-smi in a terminal does the same. Watch the card during model load, not just after the generation finishes, because that is the moment the issue reporter saw the limit being violated.

    A quick diagnostic sequence:

    # 1. Baseline: what is already using VRAM before ComfyUI starts?
    nvidia-smi --query-gpu=memory.used,memory.total --format=csv
    
    # 2. Start ComfyUI with your chosen flag
    python main.py --vram-headroom 2
    
    # 3. Watch peak usage during a real run, not an idle one
    watch -n 1 nvidia-smi

    If peak usage still climbs past your intended ceiling, the honest answer with the current codebase is to fall back to --disable-dynamic-vram and re-test, or to close the other VRAM consumer rather than fight the allocator.

    Don’t Confuse This with Offloading or Pinned Memory

    It is easy to lump every memory flag together, but they address different failure modes. --vram-headroom and --reserve-vram are about governing how much of the card ComfyUI is allowed to hold. They are separate from:

    • Offload behavior — --lowvram, --novram, --gpu-only, which control where models live (and --lowvram is now a no-op under Dynamic VRAM).
    • Pinned-memory OOM — a distinct video-model failure fixed with --disable-pinned-memory, which I documented in a separate article.
    • System RAM pressure — the OOM-killer and swap problem, which is about host memory, not VRAM, and needs different handling.

    Keeping these buckets separate is what prevents you from chasing the wrong flag for an hour while VRAM keeps climbing.

    Conclusion

    The short version: if you updated ComfyUI and your old --reserve-vram cap stopped holding, you are hitting a real, documented behavior change, not a typo in your launch command. The new --vram-headroom flag is the one explicitly designed to keep a hard slice of VRAM free above the default headroom while counting other apps’ usage — but even it has open questions around whether it allocates that headroom up front.

    Before you change anything, decide which problem you have (leave room for other apps vs. reserve for the OS), apply the matching flag, and verify with nvidia-smi during model load rather than after the fact. And because this is an active area of the codebase — the --reserve-vram issue is still open and assigned — re-check the official startup-flags reference after your next update, because these two flags are exactly the kind of thing that changes between point releases.

  • ComfyUI Is Wasting Your VRAM: How –reserve-vram (Even Negative Values) Reclaims GPU Memory

    If you upgraded ComfyUI recently and noticed your generation times got slower while nvidia-smi showed your GPU sitting at half capacity, you are not imagining it. The shift to dynamic VRAM and async offload as default behaviors changed how ComfyUI decides how much video memory to use — and on a surprising number of setups, it now under-utilizes the card instead of maximizing it.

    This guide explains what the memory flags actually control, why “just disable dynamic VRAM” is not always the right answer, and how the --reserve-vram flag — including its counterintuitive negative values — can force ComfyUI to use the GPU memory you already paid for. Everything below is documentation analysis and current maintainer guidance, not fabricated benchmark numbers.

    Why your GPU sits idle after the update

    ComfyUI’s memory management has changed in three ways that interact in non-obvious ways:

    • Dynamic VRAM is now on by default. Historically ComfyUI loaded entire model weights into VRAM up front. With dynamic VRAM enabled, it loads weights in smaller chunks and offloads them to system RAM when the card would otherwise exceed its budget. This is great for preventing out-of-memory (OOM) crashes, but it means the GPU is often kept deliberately below full occupancy.
    • Async offload is now on by default — but not everywhere. Async offload moves weights the model is about to use onto the GPU ahead of time, overlapping the transfer with in-flight computation. The catch, confirmed by maintainers, is that it is not implemented for ComfyUI-GGUF. So users running quantized GGUF models get none of the speedup, and can even feel like the new default is slower than the old manual offload behavior.
    • VRAM estimation became more conservative. To avoid the OOM reports that flooded the issue tracker, the memory estimator now leaves more headroom. The net effect for users who had a tuned, almost-full-card setup is that ComfyUI now “plays it safe” and leaves 25–50% of the card unused.

    The result is the regression described in Comfy-Org issue #11081: GGUF models that used to run at 80–90% VRAM now sit at ~45–55%, with generation speed that feels slower despite the lower memory pressure.

    Diagnosing whether you actually have this problem

    Before you change any flag, confirm the symptom. This is a two-minute check, and it is the difference between fixing a real problem and cargo-culting a launch argument you don’t need.

    Run your normal workflow and, while it is sampling, open a second terminal and run:

    nvidia-smi --query-gpu=memory.used,memory.total,utilization.gpu --format=csv

    You are looking for two patterns:

    1. Low memory utilization. If memory.used is stuck well below memory.total (say, 8 GB used on a 16 GB card) while the model is actively sampling, ComfyUI is intentionally holding back.
    2. Low GPU utilization paired with high system RAM usage. If the card is idle while your system RAM climbs, weights are being offloaded to CPU and streamed back in — the classic offload-thrash signature. The earlier post on ComfyUI system RAM OOM walks through the RAM side of this same mechanism.

    If instead your card is pegged near 100% and times are fast, the defaults are working for you — leave them alone. The flags below are for people whose card is demonstrably being under-used.

    The –reserve-vram flag and how negative values work

    --reserve-vram tells ComfyUI to keep a fixed amount of GPU memory in reserve rather than using it for model weights. Its original purpose is to leave room for your operating system, display manager, browser, or other applications that share the GPU, so that ComfyUI doesn’t grab every last byte and starve your desktop.

    It accepts a value in gigabytes, for example:

    python main.py --reserve-vram 1.5

    This reserves 1.5 GB for everything else and lets ComfyUI fill the remainder. So far, so intuitive. The part that surprises people is that the value can be negative. A Comfy-Org maintainer, responding directly to a performance-regression report in issue #11568, suggested exactly this:

    “Try varying the --reserve-vram setting to see if it helps. You can go negative if you want it to use more VRAM than the default.”

    A negative reserve effectively flips the flag from “keep this much free” to “use this much extra beyond the estimator’s conservative guess.” For example, --reserve-vram -1 nudges ComfyUI to treat the card as if it had roughly an extra gigabyte of usable space, pushing the memory allocator back toward full occupancy. The trade-off, as the maintainer’s phrasing implies, is that you take on the risk the estimator was trying to protect you from: a possible OOM.

    Think of it as a dial between two failure modes. Too much reserve wastes capacity; too aggressive a negative value invites CUDA out of memory. The right setting is the most negative value your specific workflow tolerates without crashing.

    How to tune it safely (step by step)

    The goal is to find the sweet spot where your card runs near full capacity without OOM. This is an iterative process, not a one-shot fix, and it depends entirely on your model, resolution, and workflow:

    1. Baseline first. Run your workflow with default settings and record nvidia-smi peak memory and iteration time. You need numbers to know if a flag actually helped — “feels faster” is not a measurement.
    2. Reduce the reserve gradually. If you previously set --reserve-vram 1 or higher, first try lowering it toward 0 and re-measure. For many users, simply dropping an unnecessarily high reserve is enough.
    3. Step into negative territory slowly. Start at --reserve-vram -0.5, then -1, re-running your workflow each time. Watch for two things: peak VRAM (should edge closer to memory.total) and any OOM error.
    4. Back off at the first OOM. If a workflow that used to run cleanly now throws CUDA out of memory, you have passed the safe limit. Move back up one step and keep that value.
    5. Confirm the fix persisted. Re-launch and verify the flag is being picked up — ComfyUI prints its startup arguments to the console, and you can always run with --help to confirm the flag name for your version, since flags have shifted across releases.

    Two caveats worth stating plainly. First, negative reserves are a workaround, not a promised interface — the maintainer framed it as something to try, and its behavior can change between versions. Second, aggressive negative values on a card that is also driving your display can make the entire desktop stutter or kill other GPU apps, because you are deliberately reducing the headroom those apps rely on.

    When to use native formats instead of fighting dynamic VRAM

    Here is an important nuance many guides skip: the Comfy-Org team’s own recommendation is not “disable dynamic VRAM” but “try native formats instead.” A changelog entry for the dynamic VRAM work is explicit — the project added a prompt to “try native formats instead of disabling dynamic VRAM.”

    The reason ties back to the GGUF gap. Dynamic VRAM and async offload work best with native (non-GGUF) model formats such as fp8, which are what the core team optimizes against. GGUF, which lives in a separate library (ComfyUI-GGUF), does not yet get the async-offload or memory-pinning benefits. So if you are running a GGUF quantized video model and seeing under-utilization, you have two coherent paths:

    • Stay on GGUF and tune memory by hand — using --reserve-vram (possibly negative) to push occupancy back up, accepting that you manage the OOM risk yourself.
    • Switch to the native fp8 weight if one exists for your model, letting the new memory manager do what it was designed to do. This is often the lower-friction fix in the long run, at the cost of a larger download and slightly different quality/speed characteristics.

    There is no universally correct answer — it depends on whether a native weight is available for your model and how much VRAM you have to spare. What is universal is the diagnosis: know whether your card is actually under-used before you reach for a flag at all.

    Pitfalls that make people think the flag “doesn’t work”

    A few common failure modes make tuning look ineffective when the real problem is elsewhere:

    • Wrong config is being read. If you are on Comfy Desktop, launch flags in the command line may be overridden by desktop settings, or vice versa. Confirm which configuration actually governs your instance before concluding the flag is broken.
    • The bottleneck isn’t VRAM. Offload improves memory, not raw compute. If your card is memory-constrained but also compute-bound, reclaiming VRAM may not speed anything up at all. Check GPU utilization, not just memory, before attributing slowness to memory.
    • Text encoder on CPU. A separate regression had text encoders (like umt5) running on CPU, which tanks encode time regardless of how much VRAM you reclaim. If nvidia-smi shows ~0% GPU during the encode step, the problem is device placement, not memory budget.
    • Mixing flags that fight each other. Combining --reserve-vram with --lowvram or --novram produces contradictory instructions to the allocator. Use one memory strategy, not several at once. The earlier guide on ComfyUI offload flags explains what each mode does before you stack them.

    Conclusion

    The move to dynamic VRAM and async offload made ComfyUI dramatically more robust for memory-constrained setups, but the trade-off is that some cards now run well below capacity by default — and GGUF users, in particular, get the conservative side of the deal without the async-offload payoff. Before reaching for a fix, measure your card. If it is demonstrably under-used, --reserve-vram is the surgical dial: lower your existing reserve first, then step into negative values cautiously, backing off at the first OOM.

    If a native fp8 weight exists for your model, that is often the cleaner long-term fix than hand-tuning a negative reserve — it lets the new memory manager do what it was built to do. Whichever path you take, the guiding principle is the same: treat VRAM tuning as a measured experiment with a recorded baseline, not a guess.

  • ComfyUI Dynamic VRAM: What It Is and How to Fix the OOM and Broken-Node Errors It Causes

    If you updated ComfyUI recently and suddenly started seeing CUDA out-of-memory errors on a card that ran the same workflow fine a week ago, you are not alone — and the regression may not be your workflow at all. The culprit is a feature called Dynamic VRAM, and since a recent stable release it ships enabled by default on NVIDIA hardware on Windows and Linux.

    Dynamic VRAM is a genuine, well-engineered improvement to how ComfyUI manages model weights in memory. In normal operation it lowers system RAM usage, removes a whole class of out-of-memory crashes, and speeds up model loading. But because it replaces the old memory manager with a completely different allocation strategy, it also introduces a handful of sharp, reproduce-able failure modes — especially for users running custom nodes that assume the old allocator’s behavior.

    This article explains what Dynamic VRAM actually does under the hood, how to recognize when it is the thing breaking your workflow, and exactly which flag to use when you need to turn it off. It is a documentation-based reference, cross-checked against the official ComfyOrg announcement and the project’s startup-flag reference; it does not fabricate benchmarks or first-run performance numbers.

    What Dynamic VRAM Actually Changes

    Before this feature, ComfyUI used an estimate-based memory manager. It tried to predict how much memory a model would need before inference, then kept enough VRAM free so that the operation could complete without running out. That prediction was inherently brittle — get it wrong in one direction and you waste VRAM, get it wrong in the other and you crash with an OOM.

    Dynamic VRAM throws the prediction out entirely and replaces it with a just-in-time allocator the ComfyOrg team calls the AI Model Dynamic Offloader (aimdo). When you load a model, ComfyUI creates a structure called a Virtual Base Address Register (VBAR). Creating a VBAR costs zero physical VRAM — it only consumes GPU virtual address space, which is effectively free and unlimited. The actual model weights are “faulted in” only at the exact millisecond a given layer is needed for a computation.

    That is the key shift. In the old system, loading a model meant committing its physical VRAM up front. In the new system, VRAM is consumed lazily, layer by layer, as the sampler actually touches each weight. When VRAM fills up and a new weight has no room, ComfyUI does not crash — it copies just that single layer into a temporary regular GPU tensor, executes the operation, then frees it. The result, in the ideal case, is that the largest models can run on the smallest budgets without depending on your operating system’s slow page file.

    The new RAM behavior is also worth calling out. ComfyUI now uses its own safetensors loader that maps files to uncommitted, file-backed memory rather than deep-copying weights into a PyTorch model. On Windows you may still see high RAM usage in Task Manager because ComfyUI keeps what it can for speed — but, unlike the old design, those cached weights will never be pushed to the page file, and the OS can reclaim the memory the instant another application needs it.

    How to Recognize a Dynamic VRAM Problem

    The tricky part is that Dynamic VRAM failures rarely announce themselves by name. They surface as ordinary-looking errors on hardware that worked before. Here are the most commonly reported symptoms, drawn from the official announcement thread and linked GitHub issues.

    Symptom 1 — TensorRT nodes throw a CUDA allocator error. If you use ComfyUI_TensorRT or ComfyUI-Rife-Tensorrt, you may see a trace that ends in something like:

    [E] [defaultAllocator.cpp::nvinfer1::internal::DefaultAllocator::allocate::53]
    Error Code 1: Cuda Runtime

    TensorRT ships its own allocator and does not play well with ComfyUI’s new dynamic one. The ComfyOrg team has explicitly confirmed this as a known incompatibility. This one is unambiguous: if you rely on TensorRT and the error starts after an update, Dynamic VRAM is the prime suspect.

    Symptom 2 — OOM on cards that had plenty of headroom. Several users on high-end hardware (including one on an RTX 4090 running klein9b) reported new out-of-memory errors immediately after updating, on workflows that “worked perfectly” before. New OOM after an update — especially with no other change — is a classic regression signature.

    Symptom 3 — RAM fills up and never comes back down. A GitHub issue titled “Maxed Out RAM, ComfyUI Is Not Offloading Properly” documents VRAM, RAM, and eventually swap being exhausted, with ComfyUI’s own Settings → About panel still reporting whatever free RAM it saw at launch. Users report that --cache-ram 16 and --cache-ram 32 do not reliably fix it, and that native (non-Docker) installs behave the same as containerized ones.

    If you hit any of these, the first step is to isolate the variable: reproduce the failure on a clean workflow with custom nodes disabled. That is the same discipline ComfyOrg itself asks for in their troubleshooting guide — rule out custom nodes before touching core behavior.

    The Fix: Disable Dynamic VRAM for the Affected Session

    ComfyUI exposes a dedicated startup flag to turn the feature off. According to the official startup-flags reference, the flag is simply:

    python main.py --disable-dynamic-vram

    This reverts ComfyUI to the previous estimate-based model loading behavior. Add it to whatever launch command you already use — for example, a typical manual launch becomes:

    python main.py --disable-dynamic-vram --listen 0.0.0.0 --port 8188

    If you launch through the desktop application or a portable distribution, the same flag belongs in your launcher’s command-line or config, not in a workflow. It is a startup flag, so you must fully restart ComfyUI for it to take effect — changing it mid-session does nothing.

    For the inverse situation — hardware where Dynamic VRAM is not auto-enabled but you want to force it — there is a matching --enable-dynamic-vram flag. The reference notes it is automatic on NVIDIA and on ROCm 7.14+; WSL is currently not planned for support.

    Adjacent flags worth knowing

    Two other flags sit right next to this one and are often confused with it. They do not disable Dynamic VRAM, but they change its behavior in the narrow ways their names suggest:

    • --vram-headroom GB — reserves extra VRAM above ComfyUI’s default headroom, so the allocator tries to keep that many gigabytes completely free even counting VRAM used by other apps. Increase this if your desktop compositor or a browser is competing for the same card.
    • --reserve-vram GB — reserves VRAM for the OS and other software explicitly, separate from the dynamic headroom.

    These give you finer control than a blunt on/off switch. If your only issue is that the OS stutters while ComfyUI runs (rather than a hard crash), try --vram-headroom 2 before reaching for the nuclear --disable-dynamic-vram.

    Why You Might Not Want to Disable It

    Before you pin --disable-dynamic-vram permanently, consider what you are giving up. The old estimate-based manager existed precisely because it allowed some control over predictable memory use; the new allocator removes an entire category of crashes and, on memory-constrained hardware, is the difference between running a large model and watching it page out to disk.

    The official announcement lists concrete wins: lower system RAM usage, faster initial model loads and LoRA application, the ability to run models that exceed your physical RAM, and — importantly — the elimination of the page-file bound model unloading that used to plague multi-model workflows. If your workflow is GPU-light and you use no TensorRT nodes, Disabling it may cost you speed for no benefit.

    A healthy rule of thumb: leave Dynamic VRAM on by default, and only disable it when you can attribute a specific failure to it. The three symptoms above give you concrete attribution clues. When in doubt, the fastest diagnostic is a binary test — run the exact same JSON workflow once with default flags and once with --disable-dynamic-vram, and compare which one completes.

    Verifying the Diagnosis

    How do you confirm Dynamic VRAM is really your problem, rather than a genuinely OOM-bound workflow? Run this short checklist before you file any bug report:

    1. Reproduce on a clean workflow. Load the default text-to-image template with no custom nodes. If it runs clean while your custom workflow fails, the problem is node-level, and disabling Dynamic VRAM may or may not be the right fix — but you have isolated the layer.
    2. Flip the flag once. Re-run the failing workflow with --disable-dynamic-vram. If it completes, the feature was the trigger. Note that this is a workaround, not a root cause — report it upstream so the incompatibility gets fixed.
    3. Check the About page. If RAM is being exhausted, note whether Settings → About reports a free-RAM figure that no longer matches reality. That stale reading is itself a known symptom documented in the RAM-offloading issue.
    4. Capture full logs. ComfyOrg’s own bug-report guidance asks for full logs, the workflow JSON, and your OS/GPU/PyTorch version. Screenshots of just the red error text are rarely enough to diagnose a memory-manager interaction.

    If your issue is TensorRT-specific, the workaround is effectively mandatory until the maintainers of those nodes update them for the new allocator — the --disable-dynamic-vram flag is the documented stop-gap.

    Conclusion

    Dynamic VRAM is a real improvement, not a bug — but shipping it enabled by default means its rough edges are now yours to trip over. The telltale signs are TensorRT allocator errors, unexplained OOM on hardware that previously had headroom, and RAM that climbs without ever coming back down. In each case the fast, reversible diagnostic is the same: restart once with --disable-dynamic-vram and see whether the failure disappears.

    Keep the feature on when your workflow is healthy, because it is genuinely faster and less RAM-hungry for most pure-SD or SDXL pipelines. Reach for the off switch only when you have attributed a specific crash to it — and when you do, open an issue upstream with full logs so the incompatibility gets fixed on the real side of the equation.

    If you are tuning memory on a constrained card more broadly, our guides to ComfyUI’s VRAM launch flags and to running the Wan 2.2 Animate 14B on 16 GB via BlockSwap cover the surrounding model-loading landscape. For the authoritative behavior of every flag referenced here, see the official ComfyUI startup-flags reference and the ComfyOrg Dynamic VRAM announcement.

  • ComfyUI ‘This action is not allowed with this security level configuration’: How to Fix It Safely

    You open ComfyUI-Manager, click Install on a custom node, and instead of a progress bar you get a red line: This action is not allowed with this security level configuration. You then edit config.ini, set security_level = weak, restart — and nothing changes. Or you edit the wrong config.ini because there are now two possible locations. This error is one of the most commonly misdiagnosed problems in ComfyUI today, in large part because the fix has shifted across several versions and the naming has quietly changed underneath everyone.

    This guide walks through what the error actually means, how to read the security-level model correctly, where your config.ini really lives, and — most importantly — how to unblock yourself without defaulting everything to weak and silently exposing your install to the exact supply-chain risks you installed this guard against. I keep the reasoning tied to the official ComfyUI-Manager documentation and issue tracker rather than memory, because the paths and values have changed more than once.

    What the error actually means

    ComfyUI-Manager ships with a feature-level security model. Rather than a single on/off switch, it classifies every management action into a risk level, then gates those actions behind a security level you configure. When you see This action is not allowed with this security level configuration, it does not mean ComfyUI is broken — it means the action you tried is classified at a higher risk tier than your current security_level will permit.

    Per the official configuration reference, the three risk tiers map to concrete features:

    • High risk — install via git URL, pip install, installing custom nodes that are not in the default channel, and the “fix custom nodes” action.
    • Middle risk — uninstall/update, installing custom nodes from the default channel, restore/remove snapshot, and restart.
    • Low risk — updating ComfyUI itself.

    The security levels then selectively enable those tiers:

    • strong — refuses both high- and middle-risk features.
    • normal — refuses high-risk features; allows middle-tier features.
    • normal- — same as normal, but only tightens the high-risk block when ComfyUI is launched with --listen on a non-loopback address.
    • weak — allows everything.

    So the most common trigger is mundane: you tried to install a node by git URL (a high-risk action) while your install is still on the default normal. ComfyUI is behaving exactly as configured. The “fix” is to understand what you actually need to permit, not to nuke the entire guard.

    Diagnose before you edit: which config file, which version

    The single biggest source of “I changed it and nothing happened” is editing the wrong file. The config.ini location changed with ComfyUI v0.3.76 and ComfyUI-Manager V3.38, and older installs that predate the change still carry the old path. Check both:

    • ComfyUI v0.3.76+ (with the System User API): <USER_DIRECTORY>/__manager/config.ini
    • Older versions: <USER_DIRECTORY>/default/ComfyUI-Manager/config.ini

    Here <USER_DIRECTORY> defaults to ComfyUI/user and can be overridden with --user-directory. The reliable way to confirm which path your install uses is to read the Manager startup log — ComfyUI prints the exact config.ini path at launch. If you edited a file and saw no effect, there is a strong chance you edited a stale copy in the old location while your running install reads the new one (or vice versa).

    A second, subtler failure mode shows up on older ComfyUI-Manager versions. Historically the setting was named disable_unsecure_features, and it was later migrated to security_level. Some installs carried a leftover disable_unsecure_features = False line into the new config, and because the new code ignores that legacy key entirely, users believed they had “disabled the security” when they had not. The documented resolution from the issue tracker is simply to remove the legacy disable_unsecure_features line and rely on security_level.

    The safe fix: match the level to the action

    Once you have located the correct config.ini, open the [default] section and set the value that corresponds to what you are trying to do. The key principle: use the minimum level that unblocks you.

    [default]
    security_level = normal

    If you only need to install a node from the default channel, the default normal already permits it (middle-tier). You hit the error only because you used a high-risk path — git URL, pip install, or a node outside the default channel. In that specific case the documented recommendation is:

    [default]
    security_level = normal-

    The normal- tier is the useful middle ground most people are actually looking for: it keeps the high-risk block in place only when ComfyUI is exposed over the network with --listen (the exact scenario where an attacker could drive git-URL installs remotely), while allowing the full high-risk feature set on a purely local, loopback install. This is materially safer than jumping straight to weak.

    Only drop to weak if you genuinely need unrestricted git-URL and pip install access on a networked install, and understand the tradeoff. The official docs are explicit that weak means all features available, and the high-risk bucket exists specifically because those operations can run arbitrary code from the internet. If you installed a node by git URL and it pulled a malicious payload, allowing that by default is exactly how a supply-chain compromise happens. For concrete examples of what that looks like in the wild, see our write-up on malicious ComfyUI custom nodes (RATs and cryptominers) — it is the reason this guard was built in the first place.

    One more modern wrinkle worth knowing: newer ComfyUI-Manager releases have begun moving away from relying on security_level = weak alone for pip-install and git-URL installs, toward explicit opt-in flags. The README notes that if you previously relied on weak (or normal-) to enable install-via-git-URL or pip install, you must now opt in with those dedicated flags. If you set the level correctly and a git-URL install still refuses, check whether your Manager version expects a separate flag rather than assuming the level is being ignored.

    Why it locks down only on networked installs (and what that changes)

    A point that trips people up: these blocking measures “only operate when you allow non-local network connections through the --listen option.” That means the highest-risk surface is a ComfyUI server that is reachable by other machines — cloud GPU instances (RunPod, AutoDL, vast.ai, your LAN) are the canonical cases. On a cloud box, --listen 0.0.0.0 is common precisely so you can reach the web UI remotely, and it is precisely then that the security-level gate becomes both active and important.

    This reframes the fix correctly. On a cloud instance you do not want weak; you want normal- or a scoped normal at most, so a remote request cannot silently trigger a git-URL install. On a purely local, single-user desktop that never binds --listen, tightening to normal- gives you the full workflow freedom with essentially no additional attack surface. Matching the level to where ComfyUI is exposed is the difference between “I fixed the error” and “I fixed the error and left a remote-install hole.”

    Verifying the fix actually took

    After editing and restarting, don’t just re-click the install button — confirm the setting is being read from the location your running build actually uses:

    1. Restart ComfyUI and read the startup log. Confirm the config.ini path it reports matches the file you edited.
    2. Confirm the effective level. In older UI builds the Manager header or a tooltip shows the active security level; if you edited the wrong file, the level shown will not match your edit, which is your signal.
    3. Retry the exact action that failed (same node, same install path). If a git-URL install still refuses under normal-, check for the newer explicit opt-in flag described above before escalating to weak.

    If the error persists across a correct edit and restart, stop changing the level blindly and instead confirm your ComfyUI-Manager version against the issue you hit — the meaning of security_level, the config path, and even whether a given tier is honored have all changed across releases, and a stale Manager build can silently ignore settings that a current build respects. Updating ComfyUI and ComfyUI-Manager together is often the real fix when a value “doesn’t work.”

    Key takeaways

    • This action is not allowed with this security level configuration means you attempted a higher-risk action than your current level permits — usually a git-URL or pip install on the default normal level.
    • Locate the correct config.ini first: __manager/config.ini on v0.3.76+, default/ComfyUI-Manager/config.ini on older builds.
    • Prefer normal- over weak: it unblocks high-risk installs on local installs while keeping the guard active on --listen networked instances.
    • Remove any legacy disable_unsecure_features line — it is ignored after the migration to security_level.
    • These gates matter most on cloud/LAN installs where --listen exposes the web UI to other machines.

    For the authoritative reference on every option — including the full config.ini key table, network modes, and the protected __manager path — see the official ComfyUI-Manager configuration documentation, and the Manager troubleshooting guide for the security-policy section. If you are fixing this on a networked setup, also review our ComfyUI API nodes sign-in guide to understand how remote access and authentication interact with what the security level is protecting.

  • Malicious ComfyUI Custom Nodes: How a Fake Node Pack Installed a RAT and Cryptominer

    On September 29, 2026, a detailed security report was filed against a published ComfyUI custom node pack. The allegation is the kind of thing that keeps every self-hosted AI operator awake at night: a node pack installed through the official Comfy Registry ended up deploying a remote access trojan (RAT) and a cryptominer that ran unnoticed on the victim’s machine for at least nine days.

    This is not a theoretical concern. ComfyUI’s node ecosystem has grown to well over a thousand community extensions, and the platform’s own history includes prior incidents where exposed instances were turned into cryptomining and proxy botnets. Understanding how a supply-chain compromise actually plays out — and what concrete steps you can take to detect and defend against it — is now a basic operational skill for anyone running ComfyUI locally.

    This article is a documentation analysis of the public security report and adjacent industry research. It does not claim independent reproduction of the malware, but it pulls together the verifiable indicator-of-compromise (IOC) list, the attack mechanism, and the hardening steps that follow directly from the documented behavior.

    What the Security Report Alleges

    The report, filed as Comfy-Org/ComfyUI issue #16631, describes a Windows ComfyUI Desktop user who installed a node pack called champdev-comfyui-nodes version 0.5.2 through ComfyUI-Manager. The install method was cnr — meaning it was pulled from the Comfy Registry rather than manually cloned from a GitHub repository. That distinction matters, because many users reasonably assume that anything arriving through the official Manager is vetted.

    The investigation traced a timeline that is worth reading in full. Twenty-five seconds after the pack was installed, pip installed a legitimate dependency (pywinpty, a real PyPI wheel that was not itself poisoned) that the malicious nodes would later abuse to spawn terminal sessions. Roughly seventeen minutes after that, an unsigned 18 MB executable named comfyui.exe appeared in the ComfyUI output directory and began connecting out to a mining pool at prl.kryptex.network:7048.

    Over the following days, the attack escalated. A prestartup_script.py was written into the installed node’s directory — a persistence mechanism that the ComfyUI-Manager executes on every startup. Seven additional fake node packs with innocuous names (comfyui-protocolsuite, -technologysuite, -hardwaresuite, and so on) were created in custom_nodes, each containing nothing but an identical copy of that same prestartup_script.py.

    By the time the machine was examined, around sixty hidden Python processes were running a command-and-control client, with their command lines disguised to look like ComfyUI/main.py and their payloads fed through stdin. Two WebSocket connections to a C2 server were established at the moment of incident response.

    The Two Attack Primitives: Why This Node Pack Was a Backdoor by Design

    Even setting aside the later-deployed malware, the report notes that the node pack itself exposed dangerous primitives as core features. It shipped a Terminal node (backed by terminal.py and pty_session.py) exposing a full shell through the ComfyUI API, and a FileManager node exposing arbitrary file read and write.

    On a ComfyUI instance bound to 0.0.0.0 without authentication — a configuration that, according to Censys research from April 2026, is distressingly common — these two nodes alone constitute a complete remote backdoor. An attacker who can reach the API can execute arbitrary commands and read or write arbitrary files on the server without ever needing to drop a single binary.

    This is the structural risk underneath the specific incident. Whether or not the pack shipped a bundled miner, any custom node in this “shell plus file manager” category turns an exposed, unauthenticated instance into an RCE target. The Snyk research team published a related deep-dive, Hacking ComfyUI Through Custom Nodes, that walks through exactly how node code can be abused to achieve remote code execution through input handling alone.

    The Indicators of Compromise (IOC) List

    If you are worried your own machine may have been affected, the most useful artifact in the report is the concrete IOC list. These are the values you can search for in your own environment:

    Command-and-control and mining endpoints

    • C2 WebSocket servers (port 8765): 78.17.213.213, 121.127.34.173, 103.20.235.215
    • Mining pool: prl.kryptex.network:7048 (wallet krxYRP8WVQ, worker win103, algorithm pearlhash)
    • Telemetry domain: comfy-nodes-telemetry.champdev.in (parent domain champdev.in)
    • Dynamic C2 feed URLs: pastebin.com/raw/SiCPwKB6, telegra.ph/my-poem-09-25, and telegra.ph/my-thoughts-09-25-2

    File hashes

    • RAT loader prestartup_script.py (7 identical copies): cc01756015453c55c7a47b2482920b0a5908d56c131bb7713ca9e72b4ee494dc
    • Updated prestartup_script.py variant: 22581298a9e0257fd493c5cff92788ed5909836336b04e15c21abc2ffa2620eb
    • Decoded C2 client payload: 0fc2713a4404fc8d2ed282ce4379e765f33a37fef49fb593c7af4e5fb8431bbc
    • Miner executable: 4c571e7357373c362272cda7f960e128e77363813ce8a5b0ab289ae2573d8010

    Persistence pack names to look for on disk

    • comfyui-protocolsuite, -technologysuite, -hardwaresuite, -opensuite, -contentsuite, -codingsuite, -connectsuite

    Note that the fake persistence packs live on disk in custom_nodes, not necessarily on the Registry — so scanning your filesystem is more reliable than checking the Manager’s installed list alone.

    How to Audit and Harden Your ComfyUI Install

    You do not need to be a security researcher to meaningfully reduce your exposure. The following steps follow directly from the documented mechanics of the attack.

    1. Search for the IOCs first

    • Check custom_nodes for the seven fake pack directory names listed above.
    • Look for a prestartup_script.py anywhere inside your installed node directories and hash-match it against the values above.
    • Scan for any unsigned *.exe that has appeared unexpectedly in your ComfyUI output directory.
    • Check your outbound connections for the C2 IPs, prl.kryptex.network, or the champdev.in domain.

    2. Do not expose ComfyUI to the internet without authentication

    If you need remote access, tunnel it (for example, through a VPN like Tailscale) rather than port-forwarding the raw server. An unauthenticated 0.0.0.0 bind is the single biggest amplifier of every exploit category listed above.

    3. Review the custom nodes you actually have installed

    Treat every custom node as code with full access to your machine. Skim its source for subprocess, os.system, socket, and pty usage before activating it. Nodes that legitimately need a shell (for ffmpeg pipelines, for example) should still be examined closely for where their command strings come from.

    4. Watch the Manager’s security tooling

    ComfyUI has introduced mechanisms to disable and remove malicious nodes, and third-party scanners like the neuregex/nodesafe project have emerged specifically because managers cannot review every pack by hand. Using a pre-install scanner as a second opinion is cheap insurance against the volume of new packs shipped every week.

    What the Report Asks Send a Clear Signal

    The requested actions in the report are as instructive as the IOC list. They ask the registry maintainers to unpublish the pack, audit the publisher’s other packs, add the loader-and-C2 indicators to automated scanning, and — most broadly — reconsider the policy of allowing shell and file-manager style RCE nodes in the first place.

    That last point is the takeaway for operators. The security of your ComfyUI install is a function of three layers: what you expose to the network, what you install, and how much you trust before verifying. None of those layers is protected by the default setup. The good news is that all three are under your control, and the audit steps above cost minutes, not hours.

    References

  • ComfyUI Wan 2.2 ‘cannot import name Wan22’ Error: Fix the latent_formats Version Mismatch

    The Error That Shows Up Only After an Update

    One of the most frustrating ComfyUI failures is the one that appears the moment you think everything got better. You update ComfyUI (or a custom node), reload a workflow that worked yesterday, hit Run, and the sampler dies with a traceback ending in:

    !!! Exception during processing !!! cannot import name 'Wan22' from 'comfy.latent_formats'
    Traceback (most recent call last):
      ...
    ImportError: cannot import name 'Wan22' from 'comfy.latent_formats'

    If you search the long tail of this exact string, you’ll find it clustered on the ComfyUI core issue tracker and the ComfyUI-WanVideoWrapper issue tracker. The common thread in nearly every report is the same: the user just updated something. What feels like a random import bug is actually a predictable version mismatch between two pieces of software that are supposed to move together but often don’t.

    This article explains exactly what the error means, why it appears after updates, how to diagnose the version drift on your own machine, and the two clean ways to fix it. No masked rewrites, no “just reinstall and pray.” The root cause is small, and once you see it, the fix is mechanical.

    What the Error Actually Means

    The message is deceptively simple. Python is telling you that some file tried to run this line:

    from comfy.latent_formats import Wan22

    and that comfy.latent_formats — a module inside the ComfyUI core itself — does not contain a name called Wan22. The error is not saying your model file is corrupt, your VAE is wrong, or your workflow JSON is broken. It is saying the importing code expects a symbol that your specific core build doesn’t ship.

    The importer in this case is almost always ComfyUI-WanVideoWrapper, Kijai’s popular package of Wan-specific custom nodes. The wrapper added support for the newer Wan 2.2 latent format relatively early, which meant it began importing Wan22 from the core once the core itself added that class. The wrapper also historically referenced WanVideoRapper (a related latent-format symbol) in its preview code.

    The consequence is a classic contract drift:

    • WanVideoWrapper (updated) imports Wan22 from core.
    • ComfyUI core (older) has no Wan22, because native Wan 2.2 support hadn’t landed in that build.
    • Result: ImportError the instant the sampler node executes.

    You can think of the wrapper and the core as two sides of a shared interface. Update only one side, and the shared symbol names stop matching. That’s why the error so reliably shows up “right after an update” — because that’s exactly when the two sides get out of step.

    Diagnosing the Mismatch on Your Own Machine

    Before you change anything, confirm which side drifted. This matters because the fix direction differs depending on what’s actually stale.

    Step 1: Read the full traceback, not just the last line

    The ImportError line tells you what name is missing, but the traceback frames above it tell you who is importing it. Look for a path like:

    File "...\ComfyUI\custom_nodes\ComfyUI-WanVideoWrapper\latent_preview.py", line 8, in <module>
        from comfy.latent_formats import Wan21, Wan22

    If the import site points at custom_nodes/ComfyUI-WanVideoWrapper, the wrapper is the importer and the question becomes whether the core is too old. This is the most common situation.

    Step 2: Check your ComfyUI core version

    Load the ComfyUI UI and look at the version, or run a quick check in the Python environment ComfyUI uses:

    cd /path/to/ComfyUI
    python -c "import comfy.latent_formats as lf; print(hasattr(lf, 'Wan22'))"

    If this prints False, my diagnosis holds: your core predates the Wan22 symbol but your wrapper already references it. If it prints True, the core is fine and you likely have a different local problem (a stale __pycache__, a second ComfyUI install on your PATH, or a wrapper that is itself partially broken).

    Step 3: Pin down how the two components update

    ComfyUI core and the wrapper update independently. Core updates via git pull, the desktop updater, or update_comfyui.bat on portable. WanVideoWrapper updates through ComfyUI-Manager (or a manual git pull inside its custom_nodes folder). Because neither tool forces the other forward, it’s trivial to end up with a new wrapper and an old core — or the reverse.

    The Clean Fix: Align Both Sides

    There are two legitimate ways out. Pick based on which side is stale.

    Fix A: Update ComfyUI core to a version that ships Wan22

    If your wrapper is newer and references Wan22, the correct long-term fix is to bring the core up to a build that includes native Wan 2.2 support. The official ComfyUI Wan 2.2 tutorial is explicit about this: native Wan 2.2 workflows require an up-to-date core, and if nodes or latent formats appear to be missing, your ComfyUI is simply outdated.

    # In the ComfyUI repo directory:
    git pull
    pip install -r requirements.txt

    On the portable/desktop builds, use the bundled updater or update/update_comfyui.bat. After the core update, confirm the symbol is present again with the hasattr check from Step 2, then restart ComfyUI fully.

    Fix B: Point the wrapper at the version that matches your core

    If you must keep an older core (for example, you have another custom node pinned to an older API), align the wrapper downward instead. Check out a wrapper commit that predates its Wan22 import, or roll the whole wrapper back through ComfyUI-Manager to a version contemporary with your core.

    What about just deleting the import line?

    You’ll see this suggested in threads — editing latent_preview.py to drop Wan22 from the import list. It can unblock a single run, but it’s a band-aid with two real costs. First, it changes the wrapper in a way that will be overwritten on the next node update. Second, it only silences the import; it doesn’t give the wrapper the actual Wan22 logic it wants, so behavior later in the pipeline may be subtly wrong. Treat the line-deletion as a temporary diagnostic, not a permanent state.

    Common Pitfalls That Make It Recur

    Even after a successful fix, the error can bounce back if you leave these traps in place.

    • Two ComfyUI installations. A lingering old portable/desktop copy on a different drive or PATH will still import the stale latent_formats. Make sure the instance you launch is the one you updated.
    • Stale bytecode. Python caches compiled modules in __pycache__. If imports behave inconsistently after a fix, clear the relevant __pycache__ directories for both the core comfy package and the wrapper, then restart.
    • Half-applied updates. A git pull that errors partway through can leave a mix of old and new files. Re-run it until it reports cleanly, and reinstall requirements.txt afterward.
    • Switching workflows mid-fix. The wrapper error and native-Wan behavior are not always the same root cause. If native workflows work but wrapper workflows freeze (a separate, well-documented issue on the wrapper tracker), the problem may be sampler/offload configuration, not the import. Keep the two symptoms distinct.

    This class of error is a cousin of the memory-management regressions we’ve covered before — for example, the pinned-memory OOM that hits Wan and H3 video models and the Dynamic VRAM default that broke workflows. They all share the same signature: a behavior is correct in isolation but breaks when a second moving part changes underneath it.

    Verifying the Fix End to End

    Don’t trust a green Run; verify the actual points of failure are gone.

    1. Import check. Run the hasattr(lf, 'Wan22') command and confirm it returns True.
    2. Node registration. After restarting ComfyUI, open the node list and confirm the WanVideoWrapper sampler nodes load without errors in the startup log.
    3. Cold run. Load a minimal Wan 2.1 or 2.2 workflow and run the sampler. A successful frame — not just a completed “prompt executed” message but an actual output image/video — is the real acceptance test.
    4. Watch for the sibling symptom. If the import error is gone but generation now hangs at the sampler or OOMs on video, you’ve moved into a different failure class; diagnose model loading and offloading separately rather than re-chasing the import.

    Conclusion

    The cannot import name 'Wan22' error is not a mysterious bug and not a broken model. It is a version contract between ComfyUI core and the WanVideoWrapper custom node that drifted when only one side was updated. Diagnose it by reading the traceback to find the importer, confirm the core’s Wan22 symbol existence with a one-line check, then align both sides — usually by updating the core to a build with native Wan 2.2 support. Keep the band-aid line-deletion as a diagnostic only, clear stale bytecode, and watch for a second ComfyUI install hiding on your disk. Once both sides speak the same latent_formats vocabulary, the workflow that “suddenly broke” will run again.

  • ComfyUI NVFP4 on RTX 50-Series: Why Your ‘2x Faster’ Model Is Running Slower Than FP8

    You bought an RTX 5090 (or 5070 Ti / 5060 Ti), read that NVFP4 quantization delivers a “~2x performance boost” over fp8 and bf16, downloaded an NVFP4 model like LTX-2 19B, and then watched it generate slower than the fp8 version you already had. If that sounds familiar, you are not doing anything wrong — you are almost certainly falling into a silent fallback that ComfyUI documents but that is easy to miss.

    The failure here is subtle: the model works. It doesn’t crash, doesn’t throw an error, doesn’t print a red warning. It just runs slower. And because the whole point of NVFP4 is speed, a slow NVFP4 run is a symptom that the acceleration path was never actually engaged. This article walks through how to confirm that, why it happens, and how to fix it, using only commands you can run on your own machine.

    What NVFP4 Actually Requires (the part everyone misses)

    NVFP4 is a 4-bit floating-point quantization format that targets the dedicated FP4 hardware in NVIDIA’s Blackwell architecture — the chips inside every RTX 50-series card and the Blackwell Pro workstation parts. When the path works, ComfyUI reports up to a ~2x speedup compared to fp8 or bf16/fp16 on the same model.

    But there is a hard dependency buried in the official announcement. Quoting the ComfyUI blog post directly: ComfyUI “only supports NVFP4 acceleration if you are running PyTorch built with CUDA 13.0 (cu130).” The critical sentence that follows is the one that causes the confusion:

    Otherwise, while the model will still function, your sampling may actually be up to 2x slower than fp8.

    Read that again carefully. If your PyTorch is a cu128 build (the current stable PyTorch at the time of writing), an NVFP4 model will load, will produce output, and will be slower than fp8 — with no obvious error telling you why. This is the silent-fallback trap, and it is the single most common reason people report “NVFP4 made my card slower.”

    This is also consistent with the official NVIDIA Blackwell setup guidance for ComfyUI, which directs RTX 50-series users to PyTorch nightly (cu130) rather than the stable cu128 build while Blackwell support matures. If you installed ComfyUI through a stable channel and never touched your PyTorch, you are likely on cu128 right now.

    Diagnosing the Fallback: Check Your CUDA Runtime First

    Before you tweak a single flag, confirm which PyTorch build you actually have. This command is the whole diagnosis in one line:

    python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"

    Run it from the same Python environment ComfyUI uses (activate the venv or use ComfyUI’s embedded Python first, depending on your install). What you are looking for is the middle value — torch.version.cuda:

    • 13.0 or 13.1 (cu130/cu131): NVFP4 acceleration is available. Any slowness you see is not a build mismatch — move on to the next section.
    • 12.8 (cu128) or lower: You are on the stable build. NVFP4 models will run, but without hardware acceleration — and can be up to 2x slower than fp8. This is your problem.
    • None: PyTorch has no CUDA at all. You have a bigger issue; NVFP4 is irrelevant until you fix the base CUDA install.

    There is also a useful second check if you want to see which CUDA architectures your build was compiled for — this tells you whether sm_120 (Blackwell) is even present in the binary:

    python -c "import torch; print(torch.cuda.get_arch_list())"

    If sm_120 is missing from that list while you are on cu128, that is the smoking gun: your PyTorch build was never compiled with native Blackwell kernels, and NVFP4 cannot be hardware-accelerated on it.

    The Fix: Moving to a cu130 PyTorch Build

    The repair is a PyTorch reinstall, not a ComfyUI change. The official guidance for Blackwell cards is to use a cu130 build, which at present means the PyTorch nightly channel — the stable release still ships cu128.

    The exact install command depends on your OS and how you installed ComfyUI, and PyTorch’s install matrix changes over time, so the safest move is to generate the correct command from the official selector rather than copy a stale one from a forum. In the same environment where you confirmed torch.version.cuda == 12.8, run the cu130 nightly install, then verify with the same one-liner from the previous section:

    python -c "import torch; print(torch.__version__, torch.version.cuda)"

    You want that middle value to now read 13.x. Only after it does should you re-run an NVFP4 model and benchmark it against fp8. A few practical cautions from real-world reports:

    • Nightly is not stable. Community reports on Blackwell note occasional crashes with cu130 nightly builds. Treat it as a trade — native NVFP4 speed versus the stability of the cu128 release. Some people keep two environments for this reason.
    • Windows portable users should prefer the official standalone Blackwell package (the cu128 50-series build) if they are not chasing NVFP4 specifically, rather than hand-mixing PyTorch into a portable install where the embedded Python can fight you during updates.
    • Reinstalling PyTorch does not touch your models or workflows. Your checkpoints, custom nodes, and saved workflows survive a PyTorch swap. Worst case you reinstall a compatible PyTorch back to cu128 and you are exactly where you started.

    When You Are Already on cu130 But Still Not Seeing the Speed

    If your torch.version.cuda already reads 13.x and an NVFP4 model is still slower than expected, the build is no longer the culprit. There is a real-world case of exactly this: a GitHub issue on the ComfyUI-LTXVideo repository reported LTX-2 19B NVFP4 (143 frames at 720p) taking ~66 seconds on an RTX 5090 32GB, versus the ~40–45 second range shown in NVIDIA’s own video guide — with CUDA 13.1 already installed and the GPU otherwise idle.

    That gap is not a build mismatch; it is the gap between a freshly-released model path and a vendor reference number. When you hit this situation, work through these instead:

    • Confirm your ComfyUI and custom nodes are genuinely current. NVFP4 support and Blackwell kernels are landing in rapid succession. An uptime-old checkout can silently lack a performance patch that matters.
    • Verify async offloading and pinned memory are active. These are now enabled by default for all NVIDIA cards, and they primarily help when weights don’t fit in VRAM. On a 32GB card running a 19B model, they matter less; but if they were disabled, you’d lose that headroom.
    • Check your PCIe link, not just your GPU. ComfyUI explicitly notes that async-offload gains scale with PCIe generation and lane count. A card in an x8 slot (or an older PCIe 3.0/4.0 motherboard) will see measurably worse offload gains than the x16 PCIe 5.0 reference numbers.
    • Measure with the same prompt and seed. “Slower than expected” only means something against a controlled baseline. Time fp8 vs NVFP4 on the identical workload before you conclude anything.

    Experiment Flags Worth Knowing (But Not Your First Move)

    ComfyUI ships experiment optimization flags under --fast, including fp8_matrix_mult and fp16_accumulation. These can unlock further speed but are documented as experimental and may affect quality or stability. The correct order of operations is: fix the PyTorch build first, confirm the baseline speed, and only then experiment with flags — one at a time, re-measuring each change. Enabling experimental flags on top of a cu128 fallback just adds noise to a problem you haven’t actually isolated.

    One more thing worth stating plainly: if a model fits entirely in VRAM and you never offload weights, async offloading and pinned memory are not going to give you a meaningful speedup. Those optimizations target the offload path. NVFP4, by contrast, is a math optimization — it changes how fast the kernels run, not just how weights are loaded. That is why getting the correct cu130 build matters more than any flag.

    Conclusion

    The overwhelming majority of “NVFP4 is slower than fp8 on my RTX 50-series card” reports come down to one thing: a cu128 PyTorch build silently falling back to non-accelerated execution. The fix is not a ComfyUI setting — it is verifying your runtime with print(torch.version.cuda) and, if it reads 12.8, moving to a cu130 build. Confirm the version change, re-benchmark against fp8 on an identical prompt, and only then chase experiment flags or PCIe topology if the gap remains.

    For the authoritative details on NVFP4 acceleration, async offloading, and pinned memory, see the official ComfyUI optimization announcement and NVIDIA’s TensorRT FP4 on Blackwell technical blog. For the RTX 50-series setup specifics and the cu130 nightly guidance, see the ComfyUI Blackwell support discussion. If you are still running into VRAM pressure while you are at it, our earlier write-up on why Dynamic VRAM breaks some workflows and the attention backend tradeoffs cover the two settings people most often crank at the same time.

  • ComfyUI API Nodes Say ‘Sign In Required’ When You Run Workflows via Python — Here’s the Real Fix

    You have a workflow that runs perfectly when you click Queue Prompt in the ComfyUI browser UI. It uses a built-in Partner Node — a paid, cloud-backed integration such as BRIA, Seedance, or a hosted image model. The minute you submit that same workflow programmatically with a Python script hitting /prompt, the job dies with a popup that reads “Sign In Required to Use API Nodes”.

    The first assumption everyone makes is that something is wrong with the script. It isn’t. You are logged in to the web UI. The workflow runs fine manually. The failure only appears in the programmatic path, and that asymmetry is the entire clue. In this guide I’ll walk through the exact root cause, the documented authentication fix, and how to verify your batch jobs actually execute.

    Why the Error Mentions a ‘Browser Login’ That Your Script Can’t See

    The confusion comes from a deep difference between how local models and Partner Nodes authenticate. A local checkpoint, LoRA, or VAE never checks who you are — it runs on your GPU and that’s the end of it. Partner Nodes are different: they are thin clients that call out to Comfy’s hosted backend, and they are billed against your ComfyUI account, not against your local server.

    When you open the browser UI and are signed in, that account session lives in your browser. The frontend carries it into every request, so the Partner Node in your manual run can resolve “who is paying for this.” A Python script calling POST /prompt over raw HTTP has no such session. There is no browser, no cookie, no account context — only a JSON payload. The node therefore cannot authenticate against your account and correctly refuses with a sign-in error.

    Key point: this is not an issue with your server’s API key, your firewall, or your script’s HTTP handling. The Partner Node authenticates against a ComfyUI platform account, which is conceptually separate from the ComfyUI server itself. That’s why “I’m already logged in in the browser” does not help — the credential simply never makes it into the programmatic request.

    Diagnosing It: Confirm It’s the Account-Key Gap, Not a JSON Bug

    Before you reach for the fix, rule out the more common JSON mistakes so you don’t chase the wrong problem. Run this quick triage:

    • Does the node work manually in the UI? If yes, the workflow structure is valid. The failure is authentication, not node wiring.
    • Does the error appear only in the API path? This is the signature of the account-session gap. If the node also fails manually, you likely have a missing model or a malformed input instead.
    • Are you submitting API-format JSON, not UI-format? Quick sanity check: your payload should be the flat prompt object keyed by node ID, not the nested workflow export. Sending UI-format JSON causes a completely different error (“does not appear to be an API workflow JSON”), so if you see the sign-in message specifically, you’re past that hurdle.
    • Does the console stop at the “preprocessing” stage? In the common report, the job posts, returns a prompt_id, enters preprocessing, then fails. That timing — accepted, then rejected right before execution — is consistent with an auth check inside the node.

    If all four point the same direction, you’re dealing with the account-key gap and can move straight to the fix.

    The Fix: Put Your ComfyUI Account API Key in the Prompt Payload

    Since mid-2025 ComfyUI has supported running paid Partner Nodes headlessly by passing your ComfyUI Account API Key inside the request itself. You no longer need a browser session. The key travels in the extra_data field of the /prompt payload, under the key api_key_comfy_org.

    First, generate the key. Create an account on the ComfyUI Platform, then create an API key. It looks like comfyui-87d01e.... This is not the same as any token you use to authenticate your own script against your own server — it’s the account credential that Partner Nodes use to bill cloud calls.

    Here is the exact request shape. Notice the key goes inside extra_data, sitting alongside (not inside) the prompt object:

    import json, urllib.request
    
    SERVER_URL = "http://127.0.0.1:8188"
    
    prompt = json.loads(workflow_with_api_nodes)  # your API-format prompt dict
    payload = {
        "prompt": prompt,
        "extra_data": {
            "api_key_comfy_org": "comfyui-87d01e28d***********************"  # your real key
        },
    }
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(f"{SERVER_URL}/prompt", data=data)
    urllib.request.urlopen(req)
    

    For a requests-based harness, wrap the same structure with the key included:

    import requests
    
    resp = requests.post(
        f"{SERVER_URL}/prompt",
        json={
            "prompt": prompt,
            "extra_data": {"api_key_comfy_org": "comfyui-..."},
        },
    )
    resp.raise_for_status()
    

    Prerequisites to keep in mind: the key must be valid, and your account must hold sufficient credits for the Partner Node you’re calling. A valid key with an empty credit balance fails differently — the call is authorized but execution errors out on billing. If you’re handling multiple clients, you can read the key from the associated user rather than hard-coding one, but the per-request extra_data placement is the same regardless.

    Common Traps That Make the Fix Look Like It Didn’t Work

    • Putting the key in the wrong level. The field must be a sibling of prompt inside extra_data. Nesting it inside the prompt object or sending it as a raw header does not authenticate the Partner Node.
    • Confusing the account key with your server API key. A ComfyUI account key is comfyui-... and is tied to platform billing. An unrelated auth token for your own instance won’t satisfy the node’s account check.
    • Reusing a UI-exported workflow untouched. The prompt you POST must be in API format. If you’re exporting from the UI, generate the API prompt via app.graphToPrompt() in the browser console rather than hand-converting.
    • Empty credits. Authentication succeeds but execution still errors. Verify your platform credit balance before blaming the key.
    • Assuming the old browser-login requirement still applies. The headless-key support landed in a specific PR; if you’re on a very old ComfyUI build, update before expecting extra_data.api_key_comfy_org to be honored.

    Verifying the Batch Actually Ran

    Once you inject the key, don’t assume success from the HTTP 200 on POST /prompt — that only confirms the job was accepted, not that it finished. Confirm the full lifecycle:

    • Read the prompt_id from the response body.
    • Poll /history/{prompt_id} until the job leaves the queue and runs.
    • Check the node outputs in the history payload — a Partner Node that completed will return its actual artifacts.
    • Purge any hard-coded key from logs or committed scripts before you share the harness.

    A working call produces a history entry with non-empty outputs for the Partner Node. If you instead see a status still stuck at preprocessing or an auth message echoed back, re-check the key placement and your account credit balance — those two, in that order, resolve nearly every remaining failure.

    Conclusion

    “Sign In Required to Use API Nodes” is not a bug in your script and not a sign that Partner Nodes can’t be automated. It’s the gap between your browser’s account session and a headless HTTP request. Close that gap by embedding your ComfyUI Account API Key in the extra_data.api_key_comfy_org field of every /prompt payload, keep your key at the right nesting level, and verify execution through /history. With that in place, you can run paid, cloud-backed nodes in fully automated batch pipelines alongside your local models — no manual login required.

    For the current, authoritative reference on the key itself and the exact Python shape, see the official ComfyUI API Key Integration documentation. If your broader pain is submitting the right JSON in the first place, that’s a separate root cause, and the difference between UI-format and API-format workflows trips up a lot of people — see my guide on why your workflow nodes vanish when you switch from UI JSON to API JSON, written for the same local-AI automation audience.

  • ComfyUI Pinned Memory OOM on Video Models: Why –disable-pinned-memory Fixes Wan and H3 Crashes

    You have 16GB or 32GB of VRAM, nvidia-smi shows gigabytes of free memory, yet your Wan 2.2 or MiniMax H3 video workflow dies with HostBuffer.read_file_slice failed followed by CUDA error: out of memory. You have already disabled custom nodes, enlarged the Windows pagefile, and tried --disable-dynamic-vram — and the crash still shows up. This is exactly the situation a growing number of users landed in across mid-2026, and the fix for many of them was one flag they had never heard of: --disable-pinned-memory.

    This article explains what pinned (page-locked) host memory actually does in ComfyUI, why it can create memory pressure instead of relieving it, how to diagnose the failure, and the three launch flags that get large video models through an otherwise clean environment. It is grounded in the ComfyUI issue tracker and the official Dynamic VRAM announcement — no fabricated benchmarks, just the mechanism and the documented workarounds.

    The Symptom: OOM With Free VRAM, Specifically on Video Models

    The signature error looks like this in the console, usually after the sampler has already started and often on the second or later run of a session:

    [ERROR] !!! Exception during processing !!!
    RuntimeError: HostBuffer.read_file_slice failed
    ...
    torch.AcceleratorError: CUDA error: out of memory

    Two things make this error frustrating to debug. First, it appears on models that previously worked — and it surfaces most often on the large video models (Wan 2.1/2.2, MiniMax H3, LTX) that stream most of their weights between system RAM and the GPU. Second, the numbers do not add up: the maintainers have logs where 57 GiB of physical RAM was free, pagefile use was under 1.5 GiB, and the disk was not close to full — yet the allocation still failed during a CUDA synchronize in the cleanup path (see Comfy-Org/ComfyUI issue #14250). That is the tell-tale sign that the failing allocation is outside PyTorch’s normal caching-allocator accounting — which points squarely at the custom offloader path (AIMDO/VBAR) and its pinned-host-memory registration.

    If your crash matches this pattern — video model, free VRAM, fails during or immediately after a weight transfer — keep reading. If instead your simple SDXL image workflow OOMs with all VRAM visibly consumed, you have a different problem and should start with your resolution and batch size.

    What Pinned Memory Actually Does (And Why It Is Not Free)

    “Pinned” (or page-locked) host memory is RAM that the operating system and the GPU driver agree cannot be paged out to disk or relocated. It exists so the GPU can copy data to and from system RAM using high-bandwidth direct memory access (DMA) without the CPU having to bounce the data through an intermediate buffer. In practice that means faster CPU↔GPU transfers — which is why it sounds like a pure win for offloading-heavy workloads.

    Here is the part that surprises people: pinned memory has a real cost. Because those pages cannot be swapped or moved, every byte of pinned host memory is a byte of RAM that is now unavailable to the rest of the system. A helpful maintainer answer in ComfyUI discussion #15527 spells out the sequence ComfyUI actually runs:

    1. The pin_memory() path allocates host buffer space.
    2. It asks the memory manager for a pin budget / registerable size.
    3. It releases extra RAM headroom first, then calls cudaHostRegister.
    4. If those checks fail, it reuses or “steals” an existing pin instead of making a new one.

    The --disable-pinned-memory launch flag bypasses that entire path. So the real takeaway is not “pinned memory is slow,” but “pinned memory is additional non-pageable RAM you are committing on top of an already RAM-hungry workflow.”

    Why Video Models Trigger This More Than Anything Else

    A text-to-image workflow does offload weights, but it does not generate the same sustained RAM demand as a video model. A large Wan or H3 run simultaneously needs:

    • Normal pageable RAM for frames, latent buffers, VAE encode/decode, and any custom-node state.
    • Pinned (non-pageable) host memory registered for the fast CPU↔GPU weight transfers.
    • VRAM for the active layers, with Dynamic VRAM or smart memory deciding what to evict moment to moment.

    When VRAM is tight, the offloader moves more of the model through system RAM — and the pinned buffer has to be sized for those weight chunks. The result, as the discussion answer puts it, is that “the pinned path can make the system less forgiving even if a large pagefile exists.” Adding more pagefile helps normal pageable allocations but does nothing to make pinned host pages cheaper, because pinned pages never hit the pagefile in the first place. That is the concrete reason users report that “expanding virtual RAM” did not stop the Wan/H3 OOM.

    Multi-GPU setups add another layer. In the current code path, an install with more than one NVIDIA GPU can end up registering pins per device, and on Windows ComfyUI may default to a single device unless told otherwise — which is why the maintainer note on issue #15255 recommends restricting to one GPU or disabling pinned memory for multi-GPU machines.

    Diagnosing It: Confirm It Is the Pinned Path, Not Something Else

    Before you flip the flag, take two minutes to rule out the cheaper explanations, because “OOM” is a catch-all that lumps together several unrelated failures:

    • Disable all custom nodes and run a bare stock workflow. If the crash vanishes, a custom node — not pinned memory — is your culprit.
    • Watch RAM, not just VRAM. On Windows open the Performance tab and look at “Committed” memory, not just the live usage number. Crashing at high-commit with a large pagefile strongly suggests the pageable/pinned split described above.
    • Note the timing. A crash that happens right as sampling starts or during an offload, with VRAM still showing free headroom, is characteristic of the pinned/offloader path. A crash that happens only after VRAM climbs to 100% is a plain VRAM-capacity problem.

    Once you have ruled those out, the cleanest confirmation is a controlled A/B: run the same workflow twice on the same checkpoint, once normally and once with the flag below. Maintainers explicitly asked for that A/B data because the failure is intermittent and hard to reproduce.

    The Fix: Which Flag to Use and When

    There are three related flags, and they do different things — pick the one that matches your setup rather than applying all three blindly.

    1. --disable-pinned-memory — the targeted fix for the host-buffer / pinned-registration OOM described here. Reported to make stock MiniMax H3 and Wan workloads complete with no change to resolution, frame count, sampler, precision, or pagefile (issue #14250). There is a small trade-off: host-to-device transfers may be marginally slower without pinned buffers, so it is best treated as “stable default for video,” not something to immediately revert.
    2. --disable-dynamic-vram — turns off the whole Dynamic VRAM subsystem. This is the broader hammer, and it fixes a separate regression where the dynamic offloader itself crashed every generation. Use it if you are on a Dynamic-VRAM regression, not specifically for pinned memory.
    3. --cuda-device 0 (or a specific index) — restricts ComfyUI to a single GPU. This is the maintainer-recommended workaround for multi-GPU machines where per-device pinning is involved. If you genuinely want all GPUs, the note suggests --cuda-device all --disable-pinned-memory instead.

    How you apply the flag depends on how you launch ComfyUI. In a plain terminal, it is simply part of the command: python main.py --disable-pinned-memory. For the desktop or portable app, add it to the launch arguments / extra arguments field. For the Windows portable install, edit the run_nvidia_gpu.bat and append the flag to the python main.py line.

    What This Tells You About Your Machine (And When to Keep It Disabled)

    The most useful lesson from the linked discussion is that disabling pinned memory is not a shameful workaround to undo later. If your machine is RAM-constrained relative to the model you run, disabling pinned memory is a legitimate production setting. Pinned memory helps when you have healthy headroom; it hurts when a 14B video model plus its buffers is already crowding your total RAM. On a 32GB–64GB machine streaming a Wan or H3 model, you can easily land on the wrong side of that line.

    If disabling pinned memory resolves your crashes but you want that transfer speed back on a smaller, easily-fitting model, you can still leave it disabled globally and accept the modest transfer cost — the reliability win on large video jobs is almost always worth more than a few percent of copy throughput.

    Conclusion and Where to Report Your Case

    Free VRAM plus an OOM on a Wan or H3 video model is now a well-understood signature: it usually means pinned (non-pageable) host memory is being registered on an already-committed RAM budget. The order to work through is: disable custom nodes → watch committed RAM → add --disable-pinned-memory → if still failing on a multi-GPU box, add --cuda-device 0 or --disable-dynamic-vram. One of these reliably gets stock video workflows completing with no change to your actual generation settings.

    If you can reproduce the failure consistently, the maintainers want your logs — the full startup log, the workflow JSON, and your exact GPU, driver, and OS versions. File the report on the Comfy-Org/ComfyUI issue tracker and reference the pinned-memory discussion so the data lands in the right place. For a deeper look at how the underlying memory system behaves day to day, see the official Dynamic VRAM announcement, and for the broader picture of how these offloading flags fit into running large models on consumer cards, check our companion guide on ComfyUI --lowvram vs --novram vs --normalvram and the block_swap and offload_device walkthrough.

  • ComfyUI “Torch Not Compiled with CUDA Enabled”: Fix the AssertionError That Keeps You on CPU

    If you have ever started ComfyUI from a fresh clone, a new conda environment, or after an update and been greeted with AssertionError: Torch not compiled with CUDA enabled, you already know how it feels: the error reads like a hardware verdict, but it almost never is one. The message is not telling you that your GPU is broken or that your driver is wrong (at least not first). It is telling you that the Python process ComfyUI just booted is running a build of PyTorch that does not include CUDA support, or that it cannot see a CUDA-capable device through the torch it actually imported.

    This article walks through what the error actually means, how to confirm which of the three or four common root causes you are hitting, and how to fix it without nuking your whole installation. No fake benchmarks, no invented output — every diagnostic command below is one you can run yourself, and the environment evidence shown here was captured on a real machine.

    What "Torch Not Compiled with CUDA Enabled" Actually Means

    ComfyUI calls torch.cuda.is_available() (or a closely related assertion) early in startup. If that check returns False, ComfyUI refuses to continue on a GPU path and raises the AssertionError we are talking about. The important thing to internalize is the distinction between two different questions:

    Is my GPU physically present and functioning? This is answered by nvidia-smi, the NVIDIA driver, and the OS — completely outside Python.

    Does the torch that THIS Python process imported see CUDA? This is answered by torch.cuda.is_available() and torch.version.cuda, and it depends entirely on *which* Python interpreter ran and *which* PyTorch wheel is installed for it.

    Almost every real-world case of this error is a failure of the second question, not the first. The GPU is fine; the Python environment is pointing somewhere wrong.

    Diagnose Before You Fix: The Three Questions in Order

    Before touching anything, capture three pieces of ground truth. On a real Ubuntu host, here is the output these commands produced:

    
    $ which -a python3 python
    /home/ubuntu/.hermes/hermes-agent/venv/bin/python3
    /usr/bin/python3
    /bin/python3
    /home/ubuntu/.hermes/hermes-agent/venv/bin/python
    
    $ python3 --version
    Python 3.11.16
    
    $ pip3 --version
    pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
    

    Look closely at that output. There are three different python3 binaries on the PATH, and the python3 --version reports 3.11.16 while pip3 --version reports it is bound to Python 3.12. That mismatch — python3 and pip3 pointing at two different interpreters — is the root cause of a huge fraction of “Torch not compiled with CUDA enabled” reports. You install torch with one interpreter, then launch ComfyUI with a different one that has no torch (or has a CPU-only torch).

    Question 1: Does the OS see the GPU at all?

    
    nvidia-smi
    

    If this prints a table with your GPU, driver version, and CUDA version, the hardware and driver are fine. If it errors with command not found or No devices were found, stop here — this is a driver/installation problem, not a torch problem, and you should install the NVIDIA driver before doing anything else. On the host used for this article there is no nvidia-smi at all, which is exactly why the next question is decisive.

    Question 2: Which Python is ComfyUI actually launching with?

    ComfyUI runs under *some* specific interpreter. If you started it from a conda env, a venv, a system Python, or a portable install, the answer changes. Capture the interpreter’s identity and its torch in one shot:

    
    python -c "import sys, torch; print(sys.executable); print(torch.__version__); print(torch.version.cuda); print(torch.cuda.is_available())"
    

    Run that with the *exact same* python (or python3) command you use to launch ComfyUI. If it raises ModuleNotFoundError: No module named 'torch', the interpreter you are launching with simply does not have PyTorch installed — the message was not about CUDA at all, there is just no torch to check. That is precisely what happened on the machine captured here:

    
    $ python3 -c "import torch; ..."
    ModuleNotFoundError: No module named 'torch'
    

    Question 3: Is the torch you DO have a CUDA build or a CPU build?

    Once import torch succeeds, the two values that matter are:

    
    import torch
    print(torch.version.cuda)      # e.g. "12.4" or None
    print(torch.cuda.is_available())  # True or False
    

    If torch.version.cuda is None, you installed the CPU-only wheel of PyTorch. This is the single most common cause of the error. The pip install torch default on many systems pulls the CPU build; you need the CUDA-indexed wheel from PyTorch.org’s command generator instead.

    If torch.version.cuda prints a version (e.g. 12.4) but is_available() is False, the torch is a CUDA build but cannot talk to a device — usually a driver/runtime version mismatch or a missing libcudart lookup.

    Fix 1: Install the Correct CUDA PyTorch Build

    The authoritative way to get a CUDA-enabled torch is not a bare pip install torch. It is the command from the official PyTorch Get Started page, which lets you pick your OS, package manager, and CUDA version and returns an index URL. On Linux with pip and CUDA 12.4, it looks like this:

    
    pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
    

    The --index-url is the part that people omit. Without it, pip installs the stock CPU-indexed wheel from PyPI, and you get torch.version.cuda == None followed by the very AssertionError this article is about.

    Fix 2: Make the Interpreter and the Installer the Same One

    The environment evidence above shows the mismatch in the wild: python3 is the venv at Python 3.11, while pip3 belongs to the system Python 3.12. If you pip install torch in that state, you may install it into 3.12 while ComfyUI launches with 3.11 (or vice versa). The fix is to always install *through* the interpreter you will launch with, using the -m pip form, which guarantees the package lands where that interpreter can see it:

    
    python3 -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124
    

    python -m pip always installs into the environment of the python you invoked. pip (or pip3) alone depends on your PATH and can silently target a different Python. When the venv has no pip module at all (No module named pip — as captured here), bootstrap it with python3 -m ensurepip or recreate the venv cleanly, then use python3 -m pip from then on.

    Fix 3: Validate the Toolchain After You Change Anything

    After reinstalling, do not trust the installer’s exit code. Re-run the ground-truth check with the same interpreter ComfyUI will use:

    
    python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"
    

    You want all three healthy: a torch.__version__ that matches what you just installed, a torch.version.cuda that is a real version string (not None), and torch.cuda.is_available() returning True. Only then relaunch ComfyUI. If ComfyUI still raises the assertion after all three are green, the remaining suspects are a launch wrapper that injects its own environment (portable bundles, run_nvidia_gpu.bat on Windows, or a CONDA_PREFIX pointing elsewhere) rather than torch itself.

    Common Traps That Fake You Out

    The portable install trap.** On Windows, the portable ComfyUI bundles its own embedded Python. Installing torch into a system Python or a separate conda env changes nothing for the portable build — you must install into the portable bundle’s own python_embeded interpreter, or switch to the git/manual install.

    The “but it worked yesterday” trap.** An update (torch or ComfyUI) can pull a CPU wheel or a different CUDA minor version. If the error appeared immediately after an update, the diff is almost always environmental, not a hardware regression.

    The conda/PATH trap.** If conda and system Python both exist, your launch shell’s PATH decides which python ComfyUI actually runs. conda activate in one terminal does nothing for a launch from a different terminal or a service manager.

    The nvidia-smi-still-shows-GPU trap.** nvidia-smi reporting a GPU does not mean your torch sees it. nvidia-smi lives at the driver level; torch.cuda.is_available() lives at the Python-package level. Both have to be healthy.

    Verification Checklist

    Work through these in order and you will land on the real cause instead of reinstalling everything blindly:

    nvidia-smi shows the GPU and driver — confirms hardware/driver, not torch.

    which -a python3 python reveals how many interpreters are in play and which is first on PATH.

    python -c "import sys; print(sys.executable)" names the exact interpreter ComfyUI will use.

    python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())" confirms torch exists, is a CUDA build, and can see the device.

    Only after all four pass, relaunch ComfyUI and confirm the error is gone.

    If you got this far and are still on CPU, the issue is outside torch (driver version, libcudart path, or a wrapper injecting its own environment), and the next step is collecting ComfyUI’s full startup log rather than re-reinstalling torch a second time.

    Conclusion

    “Torch not compiled with CUDA enabled” is a Python-environment diagnosis wearing a hardware-error costume. The GPU is almost always fine. What is broken is the chain between the interpreter you *launched* and the torch build you *installed* — a wrong python on PATH, a pip bound to a different interpreter, a CPU-only wheel pulled because the --index-url was omitted, or an update that silently swapped your torch build. Confirm the hardware once, then interrogate the interpreter, then install CUDA-enabled torch *through* that same interpreter, and re-verify with torch.cuda.is_available(). Fix those links and the assertion disappears — without touching your driver and without reinstalling ComfyUI.

    For a deeper look at what CUDA out-of-memory symptoms look like once you *are* on the GPU, see llama.cpp “CUDA Out of Memory” on a 16GB card. For the authoritative install matrix, use the PyTorch Get Started page and the torch.cuda.is_available documentation.