ComfyUI "mat1 and mat2 shapes cannot be multiplied"? Read the Numbers, Then Fix the Mismatch (2026)

comfyuistable-diffusionfluxtroubleshootinglocal-ai

This one stops the queue cold, always at the KSampler or a ControlNet node, always with a line like:

RuntimeError: mat1 and mat2 shapes cannot be multiplied (77x2048 and 768x320)

Nothing is corrupt. Nothing needs reinstalling. The error means exactly one thing: somewhere in your workflow, a model built for one Stable Diffusion family is receiving data shaped for a different family. An SD1.5 ControlNet wired into an SDXL checkpoint. A DualCLIPLoader set to sd3 while the UNet is Flux. The wrong CLIP file in the right folder. PyTorch tries to multiply a matrix of one width by a weight matrix expecting another width, and the multiplication is mathematically impossible, so it throws.

The good news: the two numbers in the parentheses tell you which component is wrong before you touch a single node. Every fix below starts from them. (Verified against ComfyUI’s official troubleshooting docs and the linked GitHub issues, October 2026.)

Step 0: Decode the shape numbers

The error always has the form (A x B and C x D). B is the conditioning width your text encoder (or ControlNet input) actually produced. C is the width the receiving layer was built to accept. When B ≠ C, you get the crash. Those widths are fingerprints of specific model families:

WidthWhat produces itFamily
768CLIP ViT-L/14 text embeddingsSD1.5 (and the CLIP-L half of SDXL/Flux)
1024OpenCLIP ViT-H text embeddingsSD 2.x
2048CLIP-L (768) + OpenCLIP bigG (1280), concatenatedSDXL — also the SD3-style pooled vector
4096T5-XXL text embeddingsFlux / SD3

The first number (A) is usually the token count: 77 is one CLIP chunk, 154 is two chunks (or positive + negative conditioning batched), and 1 or 2 means a pooled vector rather than a token sequence.

So 77x2048 and 768x320 reads as: “a 2048-wide SDXL conditioning tensor just hit a layer expecting 768-wide SD1.5 conditioning.” That exact pairing — an SDXL checkpoint with an SD1.5 ControlNet — is the single most common cause, and it’s the example ComfyUI’s own model-issues troubleshooting page uses for this error.

Fix 1: A ControlNet from the wrong family (the most common case)

Checkpoint and ControlNet must come from the same base architecture. SD1.5 checkpoints need SD1.5 ControlNets; SDXL checkpoints need SDXL ControlNets. There is no adapter node that converts between them — the cross-attention widths are baked into the weights.

A clean real-world example: a user running the InstantID pipeline (which is SDXL-only per its own README) hit mat1 and mat2 shapes cannot be multiplied (32x2048 and 768x320) — the 768 betraying an SD1.5 OpenPose ControlNet in an SDXL graph. The fix, from ComfyUI-InstantID issue #18 (closed January 2024), was replacing it with the SDXL version, thibaud/controlnet-openpose-sdxl-1.0. Same error, same cause, in the ControlNet-v1-1 discussions and with a Depth-Anything ControlNet that only ships in SD1.5 form.

Filenames lie — plenty of repackaged ControlNets drop the sd15/sdxl suffix. When you’re not sure what a .safetensors file actually is, read its header instead of trusting the name:

python3 - models/controlnet/suspect_model.safetensors <<'EOF'
import json, struct, sys
with open(sys.argv[1], "rb") as f:
    header_len = struct.unpack("<Q", f.read(8))[0]
    header = json.loads(f.read(header_len))
widths = {v["shape"][1] for k, v in header.items()
          if k.endswith("attn2.to_k.weight")}
print(widths)
EOF

Expected output: {768} means SD1.5, {1024} means SD 2.x, {2048} means SDXL. The safetensors header is plain JSON at the top of the file, so this reads a few kilobytes and never loads the model. (It inspects cross-attention input widths, so it works on SD-family checkpoints and ControlNets; Flux and SD3 DiT models use different key names and will print an empty set.)

Fix 2: Flux with the DualCLIPLoader set wrong

Flux conditions on two encoders at once: CLIP-L for a 768-wide pooled vector, T5-XXL for the 4096-wide token sequence. Two separate misconfigurations of the loader produce the mat1/mat2 crash:

The type dropdown is not flux. In ComfyUI-GGUF issue #77, a user running Flux GGUF couldn’t find a flux option and picked sd3 in the loader. Result: mat1 and mat2 shapes cannot be multiplied (1x2048 and 768x3072), thrown from Flux’s vector_in layer. Reading it with the table above: the SD3-style loader built a 2048-wide pooled vector (CLIP-L 768 + CLIP-G 1280 concatenated), but Flux’s vector_in is a 768→3072 projection that only accepts the CLIP-L half. The fix was using DualCLIPLoaderGGUF with type set to flux, clip_l in one slot and the T5 GGUF in the other. If you’re on GGUF quants for VRAM reasons, our FLUX GGUF guide covers which quant fits which card.

The same file in both slots. ComfyUI’s docs call out loading t5xxl_fp8_e4m3fn.safetensors into both CLIP slots as a known breakage — one slot must be T5-XXL, the other clip_l.safetensors. Two different encoders, two different jobs; doubling one of them produces tensors no Flux layer expects.

Fix 3: The wrong text encoder file entirely

Models that make you load text encoders as separate files (Flux, SD3, Stable Cascade) add a failure mode that checkpoint-era SD1.5 never had: grabbing the wrong encoder into models/clip/. In a Stable Cascade thread, mat1 and mat2 shapes cannot be multiplied (2x1024 and 768x8192) traced back to exactly that — a 1024-wide ViT-H CLIP file sitting where the model expected a different encoder. Replacing the file with the one from the model’s own repository fixed it.

The reliable habit: download the text encoders from the same Hugging Face repository as the model itself, not whichever CLIP file you already have lying around from an SD2 experiment. Encoder files are interchangeable only within a family.

Fix 4: SD2.x masquerading as SD1.5

If your error shows 1024 on either side — like 154x1024 and 768x320 — an SD 2.x component is in the mix. SD2.1 swapped CLIP ViT-L (768) for OpenCLIP ViT-H (1024), so nothing SD1.5 accepts its conditioning. This usually happens through a wrong download: the v2-1 file where a tutorial meant v1-5, or an SD2.1 ControlNet in an SD1.5 graph (seen here, fixed by downloading the matching version). Delete nothing — just point the loader at the right-family file.

What this error is NOT

Knowing what you can skip saves more time than the fix itself:

  • Not a VRAM problem. Out-of-memory looks completely different — see the CUDA OOM fix guide. Buying a bigger GPU changes nothing here; a 4090 throws the identical error.
  • Not a corrupt download. A truncated or wrong-format file fails at load time with a different message — usually unknown model architecture or a header error, not at sampling time.
  • Not the VAE. A mismatched VAE produces wrong colors, conv-layer shape errors, or black images — it never touches the text-conditioning matrix multiply.
  • Not a device problem. Tensors on the wrong GPU/CPU throw Expected all tensors to be on the same device, which people confuse with this one because both appear mid-sampling.
  • Rarely a ComfyUI bug. Updating ComfyUI is worth doing once (one March 2025 report in the GGUF thread cleared after an update), but 95% of reports resolve as a model mismatch.

Quick diagnostic table

Error shapeReadingFix
…x2048 and 768x320SDXL conditioning → SD1.5 ControlNet/layerSwap in the SDXL version of the ControlNet
…x768 and 2048x…SD1.5 conditioning → SDXL componentSwap checkpoint or component so families match
…x1024 and 768x…SD 2.x file in an SD1.5 graphRe-download the v1-5 file
1x2048 and 768x3072SD3-type CLIP loader feeding FluxDualCLIPLoader type → flux, clip_l + t5xxl in separate slots
2x1024 and 768x…Wrong standalone CLIP fileGet encoders from the model’s own repo

The 60-second checklist

  1. Read the two widths in the error against the table in Step 0 — they name the mismatched families.
  2. List every model-loading node in the graph: checkpoint, ControlNet, CLIP/DualCLIP loader, IPAdapter, AnimateDiff. The one whose family doesn’t match the checkpoint is your culprit; verify with the header-reading command if the filename is ambiguous.
  3. For Flux/SD3: confirm the loader type dropdown and that the two CLIP slots hold two different files.
  4. Replace the odd one out with the same-family version. Don’t delete the old file — it’s fine for its own family.

Prevention is folder hygiene: keep controlnet/sd15/ and controlnet/sdxl/ separated, keep encoder files next to the model release they shipped with, and when you adopt someone’s workflow JSON, download the exact files it names rather than substituting near-matches. ComfyUI’s docs give the same advice: all models in one workflow from one architecture family, ideally from one release. If you’re still deciding which family to standardize on, our SD vs SDXL vs Flux comparison lays out the trade-offs, and for a broader look at ComfyUI against other UIs there’s a thorough ComfyUI review on aifoss.dev.

FAQ

Can I convert an SD1.5 ControlNet to work with SDXL? No. The cross-attention dimensions (768 vs 2048) are baked into the trained weights. Conversion would mean retraining. Download the SDXL-native version — most popular ControlNets (OpenPose, Depth, Canny) have one.

Why did my workflow break when it worked last week? Three usual suspects: a custom-node update silently changed a loader’s default type, you moved/renamed model files and a node auto-selected a different one, or you updated a checkpoint to a different family while keeping old ControlNets. Check the loader dropdowns first — ComfyUI re-resolves filenames when the saved one is missing.

Does this error ever come from LoRAs? Almost never as mat1/mat2. A wrong-family LoRA usually logs lora key not loaded warnings and simply has no effect. If you see those warnings, the LoRA is mismatched, but it won’t crash the run.

I’m on Flux and both my CLIP slots are correct — what else? Confirm the type dropdown (it must be flux, not sdxl or sd3 — a few loaders silently reset it after updates), and confirm the UNet file is actually Flux and not an SD3 checkpoint with a Flux-sounding name. The GGUF loader variant has the same dropdown.

Is any of this different on AMD or Apple Silicon? No. The error is pure tensor math, identical on CUDA, ROCm, and MPS. Hardware-specific failures look different — see our HIP out-of-memory guide for the AMD side.

Sources

Last updated October 12, 2026. Model families, loader options, and node behavior change; verify against current ComfyUI docs if you’re on a much newer build.

Was this article helpful?

Get the numbers before you buy

New GPU and mini-PC benchmarks, VRAM thresholds, and price checks — sent when there's something worth acting on, not on a schedule. No spam, unsubscribe anytime.