ComfyUI on Mac: MPS Errors and What Fixes Them
Want to go deeper than this article?
Free account unlocks the first chapter of all 25 courses — RAG, agents, MCP, voice AI, MLOps, real GitHub repos.
Generating images locally? Take it further. From FLUX and ComfyUI setup to building real image pipelines and apps. First chapter free, no card.
MPS failures in ComfyUI sort into four buckets by error string, and three of them have a known fix. For NotImplementedError: The operator '...' is not currently implemented for the MPS device, set PYTORCH_ENABLE_MPS_FALLBACK=1 before you launch. For RuntimeError: MPS backend out of memory, the ceiling you hit is PYTORCH_MPS_HIGH_WATERMARK_RATIO, not your physical RAM. For Cannot convert a MPS Tensor to float64 there is no flag at all — a node allocated a float64 tensor on the GPU, and the file has to be patched. The fourth, silent output corruption with no traceback at all, is the one that no environment variable touches.
This page is a lookup table first and an explanation second. Every error string below was taken from either the PyTorch source line that emits it or a real, linked ComfyUI issue — never paraphrased, because a paraphrased error string is useless to search for. We have not counted how often each one occurs and are not going to pretend we have; the ordering below is by how quickly you can act on it.
One thing to fix before you read further, because it silently wastes people's afternoons: PyTorch reads PYTORCH_ENABLE_MPS_FALLBACK once, at the moment the MPS dispatch table is registered. In aten/src/ATen/mps/MPSFallback.mm the lookup is a static const auto, and the value decides which of two fallback handlers gets installed. Exporting it in a shell you already started ComfyUI from does nothing. It has to be in the environment of the process at launch.
Which MPS Error Do You Have?
Find your string. The "emitted by" column is where to go if you want to read the code that produced it rather than take our word for it.
| Error string (verbatim) | Emitted by | What it actually means | First move |
|---|---|---|---|
NotImplementedError: The operator 'aten::_int_mm' is not currently implemented for the MPS device. | PyTorch, mps_error_fallback in MPSFallback.mm | An op in your graph has no Metal kernel at all | PYTORCH_ENABLE_MPS_FALLBACK=1 at launch — that op runs on CPU |
TypeError: Cannot convert a MPS Tensor to float64 dtype as the MPS framework doesn't support float64. Please use float32 instead. | PyTorch, MPS_ERROR_DOUBLE_NOT_SUPPORTED in EmptyTensor.cpp | Node code asked for a float64 tensor on the MPS device | No flag helps. Needs a code fix (compute in float64 on CPU, cast, then move) |
RuntimeError: MPS backend out of memory (MPS allocated: 2.02 GiB, other allocations: 86.04 GiB, max allowed: 88.13 GiB). Tried to allocate 64.00 MiB on private pool. | PyTorch, MPSHeapAllocatorImpl in MPSAllocator.mm | You hit the allocator's high watermark, which is a ratio, not your RAM | Read the "other allocations" number first, then consider the watermark env vars |
TypeError: Trying to convert Float8_e4m3fn to the MPS backend but it does not have support for that dtype. | PyTorch dtype conversion, reported in ComfyUI #9255 and #11626 | An fp8 checkpoint; MPS has no fp8 storage dtype | Download the fp16/bf16 or GGUF build of the same model |
NotImplementedError: "compute_index_ranges_weights" not implemented for 'Half' | PyTorch CPU kernel dispatch, reported in ComfyUI #14840 | A dtype gap, not a device gap — no fp16 kernel for that op | Run that node in fp32, or use the fp32 model file |
| No error string. Output is black, banded, or progressively corrupted. | Nothing. The run reports success. | An MPS correctness bug — indexing overflow, baddbmm semantics, or bf16 attention | No env var fixes it. See the silent-corruption section below |
Note the shape of that table. Rows one and three are configuration problems you can solve from a shell. Row two is somebody else's bug that happens to be in your model's decoder. Rows four and five are dtype support gaps. Row six is a correctness bug in the backend, and it is the only one where reaching for a flag will waste your time.
Reading articles is good. Building is better.
Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.
What Does PYTORCH_ENABLE_MPS_FALLBACK Actually Do?
It swaps which handler PyTorch installs as the catch-all for every unimplemented MPS op. With the variable unset or set to "0", PyTorch registers mps_error_fallback, which raises. Set to anything else, it registers mps_fallback, which copies your tensors to the CPU, runs the op there, copies the result back, and warns once:
The operator 'aten::<name>' is not currently supported on the MPS backend and
will fall back to run on the CPU. This may have performance implications.
The full error you get without it, quoted from the same file, is:
The operator 'aten::<name>' is not currently implemented for the MPS device.
If you want this op to be considered for addition please comment on
https://github.com/pytorch/pytorch/issues/141287 and mention use-case, that
resulted in missing op as well as commit hash <sha>. As a temporary fix, you
can set the environment variable `PYTORCH_ENABLE_MPS_FALLBACK=1` to use the
CPU as a fallback for this op. WARNING: this will be slower than running
natively on MPS.
Three things worth knowing that the message does not tell you:
- Two ops ignore the variable entirely. MPSFallback.mm explicitly registers
embedding_renorm_to the CPU fallback and_slow_conv2d_forwardto a device-consistency check, regardless of what you set. If you see the CPU-fallback warning forembedding_renorm_without having enabled anything, that is why. - The cost is per call, not per model. A missing op inside a sampler loop means a round trip to CPU memory on every step. That is the difference between "slower" and "unusable", and it depends entirely on where in the graph the op sits.
- Distributed ops can never fall back. The same file states it plainly: "Please note, that distributed operators can not fall back to CPU." Not something most ComfyUI users hit, but it explains why the variable sometimes appears to do nothing.
A real example of this exact error in the wild: ComfyUI issue #15133, where the official Ideogram 4 int8 text-to-image workflow dies on an M5 Max with NotImplementedError: The operator 'aten::_int_mm' is not currently implemented for the MPS device. — int8 matmul, which is exactly the op an int8 workflow needs on every layer.
Set it the way ComfyUI actually gets launched. From a terminal:
PYTORCH_ENABLE_MPS_FALLBACK=1 python main.py
If you use the desktop app, the launch-argument setting takes ComfyUI flags, not environment variables — you need the variable in the app's environment, which usually means launching from a shell instead. The same trap appears in issue #9255, where the reporter added force-fp16, use-split-cross-attention and cpu to comfy.settings.json and ended up running the whole model on CPU rather than falling back for one op.
Which aten Operators Are Still Missing on MPS?
The authoritative list is pytorch/pytorch#141287, "MPS operator coverage tracking issue (2.6+ version)" — the successor to the long-running #77764. Its table is the useful part, because it tells you not just whether an op exists but which PyTorch release it landed in. That matters: a "not implemented" error on torch 2.7 may simply be fixed by upgrading.
Reproduced from that issue's table:
| Operator | Times requested | Added by PR | Available from |
|---|---|---|---|
aten::max_pool3d_with_indices | 16 | pytorch#156467 | 2.9 |
aten::_linalg_solve_ex.result | 15 | pytorch#146531 | 2.7 |
aten::_standard_gamma | 13 | pytorch#179228 | Nightly |
aten::_upsample_bicubic2d_aa.out | 10 | pytorch#149378 | 2.8 |
aten::linalg_qr.out | 10 | pytorch#172536 | Nightly |
aten::angle | 8 | pytorch#143449 | 2.7 |
aten::grid_sampler_3d | 7 | pytorch#160541 | 2.9 |
aten::vdot | 6 | pytorch#172840 | 2.11 |
aten::linalg_cholesky_ex.L | 5 | pytorch#146799 | 2.7 |
aten::_linalg_det.result | 4 | pytorch#146279 | 2.7 |
aten::unique_dim | 4 | pytorch#163694 | 2.10 |
aten::linalg_lu_solve.out | 4 | pytorch#167569 | 2.10 |
aten::_upsample_bilinear2d_aa.out | 4 | pytorch#145581 | 2.7 |
aten::kthvalue.values | 3 | pytorch#161817 | 2.9 |
aten::native_dropout | 3 | pytorch#162108 | 2.9 |
Every row there is now implemented somewhere. The genuinely unimplemented ops live in the companion issue, #154052, "Most requested ops for the MPS backend", which ranks by votes (unique requesters plus thumbs-up on their comments) and strikes through anything that has since landed. The entries still standing there at the time of writing are dominated by linear algebra: linalg_eig (38 votes), grid_sampler_2d_backward (35), _linalg_eigh (24), linalg_matrix_exp (18), _linalg_eigvals (13), linalg_householder_product (8), linalg_lu (5), cholesky_inverse (4), cholesky_solve (3), _cdist_backward (3) and segment_reduce (3).
Two caveats before you rely on either list. First, the two trackers disagree — linalg_qr and unique_dim appear as landed in #141287 and un-struck in #154052. Second, both are hand-maintained. #141287 itself points you at the generated MPS operators coverage matrix and is explicit that green means released and yellow means nightly-only. Check the matrix for your specific op before you conclude anything.
The op behind #15133, aten::_int_mm, shows how unfinished this can be. Two items exist for it in the PyTorch repo — issue #190337, "[MPS] Add aten::_int_mm support", closed 17 July 2026, and pull request #193153, "[MPS] Add native _int_mm support", closed on 14 August 2026 with merged: false. The PR describes a Metal 4 matmul2d fast path with a 16×16 tiled kernel fallback, so the work has been attempted in detail; it just has not shipped. Until it does, int8 workflows on a Mac need the CPU fallback.
Why Does float64 Crash on Apple Silicon?
Because Metal has no float64. This is not a missing kernel that a fallback can route around — it is a hard type check on tensor creation. From aten/src/ATen/mps/EmptyTensor.cpp:
#define MPS_ERROR_DOUBLE_NOT_SUPPORTED "Cannot convert a MPS Tensor to float64 dtype " \
"as the MPS framework doesn't support float64. Please use float32 instead."
That macro fires from TORCH_CHECK_TYPE(dtype != kDouble && dtype != kComplexDouble, ...) inside empty_mps. The moment any code path calls something like torch.arange(..., dtype=torch.float64, device="mps"), you get a TypeError — before a single kernel runs. PYTORCH_ENABLE_MPS_FALLBACK is irrelevant here, because nothing was dispatched.
The live example is ComfyUI issue #15512 — "LTX 2.5 *= TypeError on MPS (Apple Silicon) with LTX-Video VAE - float64 not supported" — where VAEDecodeTiled crashes on ComfyUI 0.32.0. The traceback lands in comfy/ldm/lightricks/vae/na_diffusion_decoder.py, in the RoPE frequency helper:
exponents = torch.arange(0, dim, 2, dtype=torch.float64, device=device) / dim
The proposed fix in PR #15515 is the general shape of the workaround for any occurrence of this: compute the float64 intermediates on the default device (CPU, which supports float64), cast the result to float32, and only then .to(device). The precision that actually matters is in the intermediate arithmetic, not in the tensor that reaches the GPU.
So when you hit this, the diagnostic question is "which file?" Read the traceback for the last frame inside comfy/ rather than the frames inside torch/. That file, at that line, is asking for a float64 tensor on your GPU, and either a maintainer patches it or you do.
Reading articles is good. Building is better.
Free account = 20+ free chapters across 25 courses, with a per-chapter AI tutor. No card. Cancel anytime if you ever upgrade.
How Do I Fix MPS Backend Out of Memory With 64GB of RAM?
Start by reading the numbers in the message rather than the fact of it. From issue #15029, on a 64 GB M4 Max MacBook Pro running a SeedVR2 template:
RuntimeError: MPS backend out of memory (MPS allocated: 2.02 GiB,
other allocations: 86.04 GiB, max allowed: 88.13 GiB). Tried to allocate
64.00 MiB on private pool. Use PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 to
disable upper limit for memory allocations (may cause system failure).
PyTorch's own allocator held 2.02 GiB. Everything else — 86.04 GiB of it — is "other allocations", memory attributed to the Metal device from outside the PyTorch caching allocator. The failing request was 64 MiB. This is not a model that is too big; it is a ceiling that was already full before the model got there.
That ceiling is computed, and you can recover its input with division. The high watermark defaults to 1.7, and it multiplies Metal's recommendedMaxWorkingSetSize for the device. So 88.13 GiB ÷ 1.7 = 51.84 GiB — that is what Metal reported as the recommended working set on that machine, and 1.7× of it is the hard cap. On a different Mac the cap will be a different number for the same reason.
The documented variables, from PyTorch's MPS environment variables reference, with the semantics filled in from the comments in aten/src/ATen/mps/MPSAllocator.h:
| Variable | Default | Documented as | What the value does |
|---|---|---|---|
PYTORCH_MPS_HIGH_WATERMARK_RATIO | 1.7 | "High watermark ratio for MPS allocator. Default is 1.7." | Hard cap, as a multiple of recommendedMaxWorkingSetSize. 1.0 = exactly the recommended max. 0.0 disables the cap entirely. Values above 2.0 are rejected with "invalid high watermark ratio" |
PYTORCH_MPS_LOW_WATERMARK_RATIO | 1.4 unified / 1.0 discrete | "Low watermark ratio for MPS allocator." | Soft limit. Above it the allocator garbage-collects and commits command buffers more often (the source calls this "adaptive commit"). 0.0 disables both behaviours |
PYTORCH_ENABLE_MPS_FALLBACK | unset | "If 1, falls back to CPU when MPS ops aren't supported." | Read once at dispatch registration; 0 and unset both mean "raise" |
PYTORCH_MPS_PREFER_METAL | unset | "If 1, uses metal kernels instead of MPS Graph APIs. Used for matmul." | Changes which matmul implementation runs |
PYTORCH_MPS_FAST_MATH | unset | "If 1, enables fast math for MPS kernels. See section 1.6.3 in the Metal Shading Language Spec." | Relaxes floating-point rules. Leave it off while chasing NaN or corruption |
PYTORCH_DEBUG_MPS_ALLOCATOR | unset | "If set to 1, set allocator logging level to verbose." | Prints what the allocator is doing — the right tool for the message above |
PYTORCH_MPS_LOG_PROFILE_INFO | unset | "Set log options bitmask to MPSProfiler." | Profiler logging, including CPU-fallback tracing |
PYTORCH_MPS_TRACE_SIGNPOSTS | unset | "Set profile and signpost bitmasks to MPSProfiler." | Instruments-visible signposts |
On raising the ratio. The message suggests 0.0 and its own parenthetical says "may cause system failure" — that is PyTorch's wording, not ours, and it is accurate. Disabling the cap means the allocator will keep asking macOS for memory until the system starts swapping or the process is killed. There is also a second, shorter form of the same error in MPSAllocator.mm that fires when the high watermark is disabled: it drops both the "max allowed" figure and the advice, because there is no longer a limit to report. If you set the ratio to 0.0 and still see an out-of-memory error, that shorter message is what you are looking at, and no ratio will help.
The more useful lever is usually the workload. Tiled VAE encode and decode expose tile size directly; smaller tiles mean smaller peak allocations. Frame count on video models drives the attention matrix quadratically. If you are here from a memory error rather than a Mac-specific one, our guide to ComfyUI out-of-memory errors and dynamic VRAM covers the offload knobs that apply on every platform, and Mac memory pressure with local LLMs covers what unified memory does when you push it.
One structural detail that explains why ComfyUI's own memory guards rarely fire first on a Mac: in comfy/model_management.py, MPS sets vram_state = VRAMState.SHARED, and get_free_memory for a device of type mps returns psutil.virtual_memory().available — free system RAM. ComfyUI's estimate of headroom and the MPS allocator's watermark are measuring different things, so the allocator's limit is what you actually collide with.
Why Is My Output Corrupted With No Error At All?
This is the category that no flag fixes, and it is worth knowing about before you spend a day on env vars. The run completes. The progress bar fills. The file saves. The pixels are wrong.
Four open reports, each a different mechanism:
| Report | Symptom | Documented mechanism |
|---|---|---|
| #14837 | Visually corrupted images or video frames, no error | A single attention matrix (batch × heads × seq_q × seq_k) reaching roughly 2^31 elements. MPS uses 32-bit indexing internally; existing chunking logic in attention.py is free-memory-based, so it never triggers |
| #15804 | Solid black video from bf16 LTX-2.x checkpoints, intermittently | torch.baddbmm(..., beta=0) on MPS does not ignore the input tensor as documented, so a torch.empty scalar containing NaN bits poisons the whole score matrix |
| #15793 | Wan 2.1/2.2 output degrading progressively across frames | Reproduces on an M4 Pro and not an M3 Pro at identical settings, across two macOS builds and torch 2.12.1→2.13.0. Reporter's conclusion: silicon-generation-dependent, independent of dtype and attention backend |
| #15010 | Wan 2.2 TI2V 5B: vertical banding and saturation from ~frame 16 | 49-frame 512×896 render on an M4 Max, ComfyUI 0.27.0, PyTorch 2.10.0. Sampling and VAE decode both complete without an exception |
The first of those is the one you can act on. The threshold in #14837 is 2^31 = 2,147,483,648 elements in a single attention matrix, and the issue gives a worked example: a 61-frame 832×640 video generation measures a ~7.68-billion-element attention matrix, which is 7.68e9 ÷ 2.147e9 ≈ 3.6× over the limit. The practical consequence is that on a Mac with a lot of unified memory, "there is plenty of free memory" stays true right up until the output is silently wrong — the more RAM you have, the further past the indexing ceiling you can go before anything stops you.
So: if a long or high-resolution video render comes back subtly wrong on a Mac, cut the frame count or the resolution and re-run before you change anything else. If the shorter render is clean, you have located the problem, and no amount of precision tuning will move it.
The second mechanism in #15804 is a useful reminder that ComfyUI's existing macOS workaround is narrower than people assume. force_upcast_attention_dtype() in model_management.py turns on upcasting for every macOS 14.5 or newer — the comment in the source reads "black image bug on recent versions of macOS, I don't think it's ever getting fixed" — but the map it returns is {torch.float16: torch.float32}. Only fp16. A bf16 checkpoint gets no upcasting from that path at all, which is precisely the gap the LTX-2.x reports fall into. If you are chasing black images rather than corrupted ones, our ComfyUI black image and NaN fixes page covers the non-Mac causes as well.
Why Do fp8 Checkpoints Fail on MPS?
Because MPS has no fp8 storage dtype to convert them into. The error is a TypeError on conversion, not a missing kernel:
Trying to convert Float8_e4m3fn to the MPS backend but it does not have
support for that dtype.
That exact string appears in #9255 (the built-in Wan 2.2 14B text-to-video template on an M2 Studio) and again in #11626 (the video_wan2_2_14B_fun_inpaint template). The fix is a different file, not a different flag: download the fp16 or bf16 build of the same model, or a GGUF quant, which dequantises to a dtype MPS does support.
Int8 has a parallel problem one layer up — the weights load, but the matmul does not exist, which is the aten::_int_mm error in #15133. Both have been attempted on the ComfyUI side and neither has landed: PR #14606, "Add FP8 to FP16 conversion for MPS compatibility", was closed unmerged on 23 June 2026, and PR #15542, "Fix asym_w4a8_int8 loading on MPS", is open and unmerged. Check both before you assume the problem is your checkpoint — but do not plan around either one.
The general rule on Apple Silicon: quantised builds designed around NVIDIA tensor cores are the ones most likely to break, and the "bigger" fp16 file is often the one that runs. That is the opposite of the advice you get for a small CUDA card, and it catches people out. If you are still choosing hardware, MLX vs CUDA for local AI explains why the Apple stack keeps diverging like this, and the Apple Silicon AI buying guide covers what unified memory buys you.
Which ComfyUI Flags Actually Matter on a Mac?
Read from comfy/cli_args.py on master. Help text is quoted verbatim; run python main.py --help and trust your own build over this table.
| Flag | Help text (verbatim) | Why it matters on Apple Silicon |
|---|---|---|
--force-fp32 | "Force fp32 (If this makes your GPU work better please report it)." | The sledgehammer for MPS precision bugs. Costs memory and speed |
--fp32-vae | "Run the VAE in full precision fp32." | Narrower and cheaper than --force-fp32; try first |
--cpu-vae | "Run the VAE on the CPU." | Diagnostic: separates a broken decode from a broken sample |
--bf16-vae | "Run the VAE in bf16." | Middle ground — bf16 keeps fp32's exponent range |
--fp16-vae | "Run the VAE in fp16, might cause black images." | Do not use this while debugging a black image |
--force-upcast-attention | "Force enable attention upcasting, please report if it fixes black images." | Already forced on for fp16 on macOS ≥ 14.5; this is how you get it on a machine that misses that check |
--use-split-cross-attention | "Use the split cross attention optimization. Ignored when xformers is used." | Changes the attention path — worth testing against #14837 |
--use-pytorch-cross-attention | "Use the new pytorch 2.0 cross attention function." | The other attention path to A/B |
--gpu-only | "Store and run everything (text encoders/CLIP models, etc... on the GPU)." | Blunt workaround for text encoders being placed on CPU under VRAMState.SHARED |
--reserve-vram | "Set the amount of vram in GB you want to reserve for use by your OS/other software. By default some amount is reserved depending on your OS." | Leaves headroom under the watermark |
--disable-smart-memory | "Force ComfyUI to agressively offload to regular ram instead of keeping models in vram when it can." | Rules out stale warm models between queued runs |
--cpu | "To use the CPU for everything (slow)." | Last resort, and a diagnostic: if CPU is correct, the bug is in MPS |
That last row is the single most valuable diagnostic on a Mac and the most under-used. If the same workflow produces correct output under --cpu and wrong output on MPS, you have proven a backend bug and can stop testing your prompt, your model file and your node packs. It is slow enough that you will only want to do it once — do it at a low step count and a small resolution.
The text-encoder placement in that table is not hypothetical. Issue #15640 documents text_encoder_device() returning cpu on Apple Silicon because MPS reports VRAMState.SHARED, which falls through to the CPU branch. For a one-shot CLIP or T5 encode nobody notices. For MiniMax Music 3, whose conditioning stage is an autoregressive transformer running 1,501 steps, the reporter measured 5.69 s/it on CPU against 1.19 it/s on MPS for the same workflow with only the load device changed. Those are their numbers on their machine, not ours — but the ratio is the point, and --gpu-only is the blunt lever that moves it today.
What This Page Does Not Claim
- Nothing here was benchmarked on a Mac by us. Every number is either quoted from a linked issue with the reporter's hardware named, read out of PyTorch or ComfyUI source, or arithmetic shown in full. We do not own an M4 Max, and we are not going to pretend otherwise.
- The op-coverage tables are hand-maintained and already disagree with each other. Treat #141287 and #154052 as starting points and confirm against the generated coverage matrix for your exact PyTorch version.
- The bug reports linked here were open when this was written, except ComfyUI #9255 and PR #14606, both closed. None of the MPS fix PRs referenced — ComfyUI #15515 and #15542, PyTorch #193153 — had been merged. Click through before you rebuild a workflow around a workaround, and note that the "available from" column in the operator table cuts the other way: the fix may already be one
pip install --upgradeaway. - Flag names drift.
comfy/cli_args.pychanges across releases. So do the MPS env vars — the PyTorch page they come from is versioned for a reason. - We have not tested whether
PYTORCH_MPS_FAST_MATHcauses any of the corruption above. It relaxes floating-point rules by design, which makes it a plausible aggravator, but plausible is not measured. It is off by default; leave it off, and do not read that as a finding.
FAQ
Where exactly do I put PYTORCH_ENABLE_MPS_FALLBACK?
In the environment of the process, before launch: PYTORCH_ENABLE_MPS_FALLBACK=1 python main.py. PyTorch reads it once, when it registers the MPS dispatch table, and uses the value to choose between a handler that raises and a handler that copies to CPU. Setting it later in the same shell, or in a ComfyUI launch-arguments field that only accepts ComfyUI flags, has no effect.
Will the CPU fallback slow everything down?
Only the ops that were missing. The fallback is registered per operator, so the rest of your graph still runs on MPS. Whether you notice depends on where the missing op sits: one at the end of a pipeline costs a single round trip, one inside the sampler loop costs a round trip per step. PyTorch's own warning puts it as "This may have performance implications", which is doing a lot of work.
Why doesn't the fallback fix my float64 error?
Because nothing was dispatched. The float64 check fires in empty_mps when a tensor is created on the MPS device, before any operator runs, so there is no op for the fallback to redirect. That error needs the calling code changed — compute the float64 part on CPU, cast to float32, then move to the device, as ComfyUI PR #15515 does for the LTX VAE decoder.
I have 64GB of unified memory. Why am I getting MPS backend out of memory?
Because the limit is a ratio, not your RAM. The default high watermark is 1.7× Metal's recommendedMaxWorkingSetSize for the device, which is not the same as installed memory. Read the "other allocations" figure in the message first: in ComfyUI #15029, PyTorch's allocator held 2.02 GiB while 86.04 GiB was attributed elsewhere, so the model was never the problem.
Is PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 safe?
It removes the cap, and PyTorch's own message appends "(may cause system failure)" for a reason — the allocator will keep requesting memory until macOS starts swapping or kills the process. Try a smaller tile size, fewer frames, or a raised-but-finite ratio first. Note also that the allocator has a second out-of-memory message for when the watermark is disabled, so 0.0 does not mean you can never OOM.
My video output is corrupted but there is no error. What flag fixes it?
None of them, most likely. ComfyUI #14837 attributes silent corruption to a single attention matrix exceeding roughly 2^31 elements, which is an MPS indexing limit rather than a setting. Reduce frame count or resolution and re-run; if the shorter render is clean, that is your answer. Verify with --cpu at low steps — if CPU is correct and MPS is not, it is a backend bug and no precision flag will change it.
Can I run fp8 checkpoints on Apple Silicon at all?
Not directly. MPS has no fp8 storage dtype, so conversion fails with a TypeError before inference starts. Use the fp16 or bf16 build of the same model, or a GGUF quant. ComfyUI PR #14606 proposes converting fp8 to fp16 on load for MPS specifically, so check whether your version already does it.
Sources
- pytorch — aten/src/ATen/mps/MPSFallback.mm (fallback registration and both error strings)
- pytorch — aten/src/ATen/mps/EmptyTensor.cpp (
MPS_ERROR_DOUBLE_NOT_SUPPORTED) - pytorch — MPSAllocator.h and MPSAllocator.mm (watermark defaults, semantics, and the out-of-memory message)
- PyTorch docs — MPS Environment Variables
- pytorch#141287 — MPS operator coverage tracking issue (2.6+ version), its predecessor #77764, and #154052 — Most requested ops for the MPS backend
- ComfyUI — comfy/cli_args.py and comfy/model_management.py
- Comfy-Org/ComfyUI issues and PRs #9255, #11626, #14606, #14837, #14840, #15010, #15029, #15133, #15512, #15515, #15542, #15640, #15793, #15804
Generating images locally? Take it further.
From FLUX and ComfyUI setup to building real image pipelines and apps. First chapter free, no card.
Go from one-off images to a real workflow
The Local Image Generation course covers ComfyUI, SDXL and FLUX properly — plus 24 more courses on running AI on your own hardware.
Liked this? 20 full AI courses are waiting.
From fundamentals to RAG, agents, MCP servers, voice AI, and production deployment with real GitHub repos. First chapter free, every course.
Build Real AI on Your Machine
RAG, agents, NLP, vision, and MLOps - chapters across 25 courses that take you from reading about AI to building AI.
Want structured AI education?
25 courses, 519+ chapters, from $9. Understand AI, don't just use it.
Continue Your Local AI Journey
- PILLARRun FLUX.1 Locally in 2026: VRAM Needs + 5-Minute Setup
- AI-Toolkit LoRA Training: FLUX.2, Z-Image & Qwen-Image
- Best GPU for Local AI Image Generation (2026): Ranked
- Best Local AI Image Models 2026: FLUX vs SDXL vs Qwen
- Chroma Local Guide: The Apache-2.0 Uncensored FLUX Model
- ComfyUI FLUX Workflow (2026): JSON Nodes Explained
- ComfyUI LoRA Not Working: Key Not Loaded Fixes
- ComfyUI Missing Node Types: Fix a Red Workflow
- ComfyUI Setup Guide: Install, Workflows, ControlNet, Flux
- Expected All Tensors on the Same Device: ComfyUI Fix
Comments (0)
No comments yet. Be the first to share your thoughts!