Summary
A checkpoint that loads fine on one ComfyUI install fails on another with:
safetensors_rust.SafetensorError: Error while deserializing header: MetadataIncompleteBuffer
The safetensors file is corrupt/incomplete. Check the file size and make sure you have
copied/downloaded it correctly.
The file is not corrupt, not incomplete, and not a bad download. It has a few bytes
after the last tensor, and whether that is fatal depends on whether DynamicVRAM is
active.
comfy.utils.load_torch_file picks one of two safetensors parsers based on
comfy.memory_management.aimdo_enabled:
# comfy/utils.py:126-141
if ckpt.lower().endswith(".safetensors") or ckpt.lower().endswith(".sft"):
try:
if comfy.memory_management.aimdo_enabled:
sd, metadata = load_safetensors(ckpt) # ComfyUI's own parser
else:
with safetensors.safe_open(ckpt, framework="pt", device=device.type) as f:
... # the strict Rust parser
The two disagree about files that have bytes after the last tensor. ComfyUI's parser
reads the header and walks data_offsets, so it doesn't care. The Rust one rejects the
file as not fully covered by tensor data.
The result is that whether a given checkpoint loads at all depends on a setting that has
nothing to do with parsing.
Reproduction
MiniMax_H3_Ref2VA_pruned_nvfp4.safetensors from
Abiray/Minimax-H3-nvfp4-INT4-INT8-Convrot
has 66 bytes after its tensor data, exactly as HuggingFace serves it — no truncation, no
bad download:
tensors: 1132
data_start: 116,008
declared_end: 12,528,636,800
file_size: 12,528,636,866
trailing_bytes: 66
Those bytes are a marker string left by the conversion tool:
b'\nL2P_bypass_MiniMax_H3_Ref2VA_pruned_nvfp4.safetensors_1785751127\n'
With DynamicVRAM active it loads. Without, it raises
SafetensorError: Error while deserializing header: MetadataIncompleteBuffer.
Who this hits
enables_dynamic_vram() (comfy/cli_args.py:312) returns False for any of:
--disable-dynamic-vram
--highvram
--gpu-only
--novram
--cpu
and main.py:251 additionally requires NVIDIA and PyTorch >= 2.8.
So the file fails for AMD, Intel and Mac users, anyone on PyTorch < 2.8, and anyone
running --highvram or --gpu-only. That last one inverts how people expect things to
break: a 24 GB card started with --highvram refuses a checkpoint that a 16 GB card on
defaults loads without complaint.
(Checked against master at a779de4.)
The error message sends people the wrong way
comfy/utils.py:147-148 catches this exact exception and says:
The safetensors file is corrupt/incomplete. Check the file size and make sure you have
copied/downloaded it correctly.
For this failure mode that advice is actively misleading. The file is neither corrupt nor
incomplete — it has extra bytes, not missing ones — and its size is exactly what the
server reports. Following the advice means re-downloading 12 GB and failing identically,
possibly more than once.
Suggested fix
I initially assumed the fix was "just use load_safetensors in both branches", but that
looks wrong on a closer read, so taking it in order of how safe it seems:
1. Fix the message (safe, small, biggest payoff per line). You already parse the
header yourself in load_safetensors (comfy/utils.py:94-95), so the check is cheap:
if the declared end of tensor data is <= the file size, the file is not truncated, and
the message can say what is actually true — there are N bytes after the tensor data and
this parser is strict about them. That alone turns an afternoon into a minute, and it is
correct whatever you decide about the parsers.
2. Fall back on the specific exception. On MetadataIncompleteBuffer, if the header
is readable and the declared range fits the file, retry leniently.
3. Use one parser everywhere. Cleanest in principle, but I don't think it's free, and
this may be why the split exists in the first place: load_safetensors imports
comfy_aimdo.model_mmap and returns tensors built with torch.frombuffer over a
read-only mapping, carrying _comfy_tensor_file_slice and _comfy_tensor_mmap_refs on
the storage. Those are exactly what the dynamic path wants and probably not what the
legacy path wants — read-only storage in particular seems likely to matter to in-place
weight patching. So I'd expect (3) to need more thought than (1) or (2), and I may be
missing the reason entirely.
If you'd rather not couple the strict path to aimdo at all, a lenient reader is about
fifty lines of stdlib and torch — header, data_offsets, frombuffer, reshape — with no
aimdo dependency. I wrote one for this and checked it tensor-for-tensor against
safetensors.load_file on a different checkpoint that the strict reader does accept — all
917 tensors matched exactly, dtypes and shapes included.
Summary
A checkpoint that loads fine on one ComfyUI install fails on another with:
The file is not corrupt, not incomplete, and not a bad download. It has a few bytes
after the last tensor, and whether that is fatal depends on whether DynamicVRAM is
active.
comfy.utils.load_torch_filepicks one of two safetensors parsers based oncomfy.memory_management.aimdo_enabled:The two disagree about files that have bytes after the last tensor. ComfyUI's parser
reads the header and walks
data_offsets, so it doesn't care. The Rust one rejects thefile as not fully covered by tensor data.
The result is that whether a given checkpoint loads at all depends on a setting that has
nothing to do with parsing.
Reproduction
MiniMax_H3_Ref2VA_pruned_nvfp4.safetensorsfromAbiray/Minimax-H3-nvfp4-INT4-INT8-Convrot
has 66 bytes after its tensor data, exactly as HuggingFace serves it — no truncation, no
bad download:
Those bytes are a marker string left by the conversion tool:
With DynamicVRAM active it loads. Without, it raises
SafetensorError: Error while deserializing header: MetadataIncompleteBuffer.Who this hits
enables_dynamic_vram()(comfy/cli_args.py:312) returns False for any of:--disable-dynamic-vram--highvram--gpu-only--novram--cpuand
main.py:251additionally requires NVIDIA and PyTorch >= 2.8.So the file fails for AMD, Intel and Mac users, anyone on PyTorch < 2.8, and anyone
running
--highvramor--gpu-only. That last one inverts how people expect things tobreak: a 24 GB card started with
--highvramrefuses a checkpoint that a 16 GB card ondefaults loads without complaint.
(Checked against
masterat a779de4.)The error message sends people the wrong way
comfy/utils.py:147-148catches this exact exception and says:For this failure mode that advice is actively misleading. The file is neither corrupt nor
incomplete — it has extra bytes, not missing ones — and its size is exactly what the
server reports. Following the advice means re-downloading 12 GB and failing identically,
possibly more than once.
Suggested fix
I initially assumed the fix was "just use
load_safetensorsin both branches", but thatlooks wrong on a closer read, so taking it in order of how safe it seems:
1. Fix the message (safe, small, biggest payoff per line). You already parse the
header yourself in
load_safetensors(comfy/utils.py:94-95), so the check is cheap:if the declared end of tensor data is
<=the file size, the file is not truncated, andthe message can say what is actually true — there are N bytes after the tensor data and
this parser is strict about them. That alone turns an afternoon into a minute, and it is
correct whatever you decide about the parsers.
2. Fall back on the specific exception. On
MetadataIncompleteBuffer, if the headeris readable and the declared range fits the file, retry leniently.
3. Use one parser everywhere. Cleanest in principle, but I don't think it's free, and
this may be why the split exists in the first place:
load_safetensorsimportscomfy_aimdo.model_mmapand returns tensors built withtorch.frombufferover aread-only mapping, carrying
_comfy_tensor_file_sliceand_comfy_tensor_mmap_refsonthe storage. Those are exactly what the dynamic path wants and probably not what the
legacy path wants — read-only storage in particular seems likely to matter to in-place
weight patching. So I'd expect (3) to need more thought than (1) or (2), and I may be
missing the reason entirely.
If you'd rather not couple the strict path to aimdo at all, a lenient reader is about
fifty lines of stdlib and torch — header,
data_offsets,frombuffer, reshape — with noaimdo dependency. I wrote one for this and checked it tensor-for-tensor against
safetensors.load_fileon a different checkpoint that the strict reader does accept — all917 tensors matched exactly, dtypes and shapes included.