**CIVITAI IS RENAMING EVERY DOWNLOADED FILE THE SAME THING. MAKE SURE YOU SAVE-AS AND NAME THE FILE SO YOU KNOW WHAT IT IS.**
Everything here is free and stays free — the format spec, the nodes, the workflows, the cartridges, the LoRAs. If it saved you a night of debugging (it contains several hundred of mine), tips keep the 5090 warm:
🔁 Liberapay (recurring)
⚡ Or right here: the Civitai tip button on this page sends Buzz directly
The text encoder half of MiniMax-H3, quantized. H3 runs on two separate models: the 33B video+audio DiT, and this — a Qwen3-VL-32B vision-language encoder. The DiT on its own will not generate anything. If you downloaded the H3 GGUF weights and had nowhere to point the CLIP loader, this is the missing file.
Load this with H3ClipLoaderAny, not “CLIPLoader (GGUF)”. ComfyUI-GGUF's own CLIP loader has no minimax type, and it silently falls back to another architecture instead of erroring — so you get a confusing crash deep inside a tokenizer (TypeError: the JSON object must be str, bytes or bytearray, not NoneType in flux.py) rather than “unknown type”. And even with the right type it still would not work: this file is Qwen3-VL-32B cut to 50 layers with no final norm and no lm_head, and its vision tower uses different tensor names than llama.cpp writes. H3ClipLoaderAny (category loaders/minimax, from ComfyUI-H3-Multishot) does that surgery on load. Set type = minimax.
The files
Q4_K_M — 19.8 GB · about 16.5 GB resident. Recommended.
Q5_K_M — 23.2 GB · higher precision, more VRAM.
mmproj F16 — 1.2 GB · the multimodal projector. Download this one too. See below.
Yes, these are real K-quants — unlike the H3 DiT, which cannot have them. K-quants need weight rows divisible by 256, and the DiT's hidden width is 2688, which is not. This encoder is a stock Qwen3-VL and its rows are, so the whole K-ladder is available here.
The mmproj is not optional — and it pairs by filename
The mmproj is the encoder's vision path. Everything that conditions on an image goes through it: image-to-video, reference-to-video, and multi-shot chaining — chaining works by feeding the previous shot's last frame back through the encoder as an image, so it needs the projector even when you never attached a reference picture yourself.
ComfyUI-GGUF locates it by matching the encoder's filename. Rename either file, or keep them in different folders, and pairing silently fails. Text-only prompts keep working, which is exactly what makes this hard to diagnose — it presents as the model ignoring your reference image rather than as a missing file.
Keep both names exactly as downloaded, in the same folder. The attached setup pack includes check_encoder_setup.py, which tells you in one line whether the pairing resolves.
Install
ComfyUI/models/text_encoders/
MiniMax-H3-encoder-Q4_K_M.gguf
MiniMax-H3-encoder-mmproj-F16.ggufLoad with CLIPLoaderGGUF from ComfyUI-GGUF, or with H3ClipLoaderAny from ComfyUI-H3-Multishot, which takes .safetensors or .gguf in one dropdown. Set the CLIP type to minimax.
No architecture patch is needed for the encoder. The H3 DiT needs minimax_h3 added to ComfyUI-GGUF's image-architecture list; this file is Qwen3-VL, and qwen3vl is already in its text list. Nothing to run.
VRAM — read this before reporting a slow render
The encoder (~16.5 GB) and the H3 DiT (~25 GB) do not co-fit on a 32 GB card. If both are held at once, the DiT loads partially and streams the remainder from system RAM on every sampling step. Measured on an RTX 5090 — 960×544, 124 frames, 20 steps, ref2va-Q5_1, one reference image: 12.3 minutes with the encoder evicted. The un-evicted run of the same render was killed past 90 minutes without finishing, so there is no honest completion time for it.
This is a cliff, not a curve: either the DiT is resident and you get normal speed, or it is not and you stream ~19 GB every step. How bad it gets depends on how far over your card you are, not on resolution or frame count directly — so do not read a single speed-up ratio as a general figure.
The tell in your log is:
loaded partially; 6423 MB usable, 5847 MB loaded, 19363 MB offloadedThe fix is to evict the encoder after conditioning and before sampling. The samplers in ComfyUI-H3-Multishot do it automatically and print [H3Multishot] TE evicted; NN.N GB free for the DiT.
What else you need to run H3
MiniMax-H3 GGUF — the DiT (
fl2vaorref2va), on Hugging Face and in my Civitai listings.Comfy-Org/MiniMax-H3 — the video and audio VAEs.
ComfyUI-H3-Multishot — multi-shot, I2V, and the encoder eviction above.
Everything else I've published
Support
Everything I publish is free and stays free. If it saved you a night of debugging, tips keep the 5090 warm: Ko-fi · GitHub Sponsors · Liberapay.
Description
FAQ
Comments (8)
Is this different to the Krea 2 TE Qwen3-VL Instruct? Would be great not to have so many different TE's..
Different model, unfortunately - same family, but about 8x apart in size.
Krea 2 uses Qwen3-VL-4B. MiniMax-H3 uses Qwen3-VL-32B.
@joeygambino Thanks! I was worried that was the answer, I kept reading it, then looking at the 24GB file and thinking, ok where does that fit exactly? Then see loads of people saying they can run on on 6Gb's of VRAM..
can you provide a workflow? I got error on cliploader (gguf)
File "execution.py", line 545, in execute
output_data, output_ui, has_subgraph, has_pending_tasks = await get_output_data(prompt_id, unique_id, obj, input_data_all, execution_block_cb=execution_block_cb, pre_execute_cb=pre_execute_cb, v3_data=v3_data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "execution.py", line 344, in get_output_data
return_values = await asyncmap_node_over_list(prompt_id, unique_id, obj, input_data_all, obj.FUNCTION, allow_interrupt=True, execution_block_cb=execution_block_cb, pre_execute_cb=pre_execute_cb, v3_data=v3_data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "execution.py", line 318, in asyncmap_node_over_list
await process_inputs(input_dict, i)
File "execution.py", line 306, in process_inputs
result = f(**inputs)
^^^^^^^^^^^
File "ComfyUI-GGUF\nodes.py", line 251, in load_clip
return (self.load_patcher([clip_path], clip_type, self.load_data([clip_path])),)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "ComfyUI-GGUF\nodes.py", line 236, in load_patcher
clip = comfy.sd.load_text_encoder_state_dicts(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "comfy\sd.py", line 1902, in load_text_encoder_state_dicts
clip = CLIP(clip_target, embedding_directory=embedding_directory, parameters=parameters, tokenizer_data=tokenizer_data, state_dict=clip_data, model_options=model_options, disable_dynamic=disable_dynamic)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "comfy\sd.py", line 260, in init
self.tokenizer = tokenizer(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "comfy\text_encoders\flux.py", line 128, in init
super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, name="mistral3_24b", tokenizer=Mistral3Tokenizer)
File "comfy\sd1_clip.py", line 696, in init
setattr(self, self.clip, tokenizer(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "comfy\text_encoders\flux.py", line 121, in init
super().__init__("", pad_with_end=False, embedding_directory=embedding_directory, embedding_size=embedding_size, embedding_key=embedding_key, tokenizer_class=MistralTokenizerClass, has_end_token=False, pad_to_max_length=False, pad_token=11, start_token=1, max_length=99999999, min_length=1, pad_left=True, disable_weights=True, tokenizer_args=load_mistral_tokenizer(self.tekken_data), tokenizer_data=tokenizer_data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "comfy\text_encoders\flux.py", line 84, in load_mistral_tokenizer
mistral_vocab = json.loads(data)
^^^^^^^^^^^^^^^^
File "json\__init__.py", line 339, in loads
TypeError: the JSON object must be str, bytes or bytearray, not NoneType
I place both files in textencoder and renamed them to this as mentioned
ComfyUI/models/text_encoders/
MiniMax-H3-encoder-Q4_K_M.gguf
MiniMax-H3-encoder-mmproj-F16.gguf
Your files and your rename are both fine - that's not the problem. The problem is the node.
Look at where the traceback lands: comfy/text_encoders/flux.py, building a "mistral3_24b" tokenizer. That's Flux 2's text encoder, not Qwen3-VL. So it never tried to load this as an H3 encoder at all.
Why: ComfyUI-GGUF's CLIPLoader (GGUF) has no "minimax" type in its dropdown - the
word doesn't appear anywhere in its nodes.py. And it resolves the type like this:
clip_type = getattr(comfy.sd.CLIPType, type.upper(), CLIPType.STABLE_DIFFUSION)
so anything it doesn't recognise silently falls back instead of erroring, which is why you got a confusing crash deep in a tokenizer instead of "unknown type". Use H3ClipLoaderAny from the ComfyUI-H3-Multishot pack instead, with type set to minimax. It's under loaders/minimax.
And to save you trying to force it: even with the right type, the plain GGUF loader can't load this file. The H3 encoder is Qwen3-VL-32B cut down to 50 layers with no final norm and no lm_head, and its vision tower uses different tensor names than llama.cpp writes (merger keys, deepstack indexing, qkv). H3ClipLoaderAny does that surgery on load - truncates the text side, pulls in the mmproj sidecar, and renames the vision keys to H3's layout. That's the whole reason the node exists.
Two more things it handles for you: mmproj files are hidden from the dropdown (you pick the main file, the sidecar is found automatically), and it scans subfolders, so a gguf/ subdirectory still shows up. Workflows are here, three of them, and they're already wired this way:
https://civitai.com/models/2833322
https://huggingface.co/joeygambino/MiniMax-H3-Multishot-Workflow
Node pack: https://github.com/jlucasmcrell/ComfyUI-H3-Multishot
Install via Manager > Install via Git URL, restart, then hard-refresh the browser tab - the frontend caches node definitions and you'll otherwise see red nodes. One last thing while you're there: the H3 DiT also needs a one-line architecture patch for ComfyUI-GGUF (run apply_gguf_arch_patch.py from the pack folder). The encoder doesn't - qwen3vl is already in its text list.
@joeygambino Thank you!
I started installing the Multishot custom node right after I posted my comment here. I used ClipLoader GGUF because I already had it installed, and you mentioned it would work too. It also has a MiniMax option in the dropdown.
I didn't fully understand all the technical details, but I installed the Multishot custom node, ran the Python file, and it's working now. Thanks for your help!
ComfyUI-GGUF locates it by matching the encoder's filename. Rename either file, or keep them in different folders, and pairing silently fails. Text-only prompts keep working, which is exactly what makes this hard to diagnose — it presents as the model ignoring your reference image rather than as a missing file.
Is this an actual fact?? If it is, its weird AF and it needs to be fixed.
What if you use ComfyUI-H3-Multishot instead? Can you rename the files no problem?