Comfy-Org/ComfyUI · error · RuntimeError

ERROR: VAE is invalid: None\n\nIf the VAE is from a checkpoi

Error message

ERROR: VAE is invalid: None\n\nIf the VAE is from a checkpoint loader node your checkpoint does not contain a valid VAE.

What it means

VAE.throw_exception_if_invalid raises when first_stage_model is None, which happens when VAE.__init__ found no recognizable VAE weights in the state dict (it logs 'No VAE weights detected' and returns with first_stage_model = None) or an explicit VAE=None was passed from a checkpoint loader. Any later encode/decode call first validates through this method, so the user gets a clear message instead of an AttributeError.

Source

Thrown at comfy/sd.py:1085

        m, u = self.first_stage_model.load_state_dict(sd, strict=False, assign=self.patcher.is_dynamic())
        if len(m) > 0:
            logging.warning("Missing VAE keys {}".format(m))

        if len(u) > 0:
            logging.debug("Leftover VAE keys {}".format(u))

        logging.info("VAE load device: {}, offload device: {}, dtype: {}".format(self.device, offload_device, self.vae_dtype))
        self.model_size()

    def model_size(self):
        if self.size is not None:
            return self.size
        self.size = comfy.model_management.module_size(self.first_stage_model)
        return self.size

    def throw_exception_if_invalid(self):
        if self.first_stage_model is None:
            raise RuntimeError("ERROR: VAE is invalid: None\n\nIf the VAE is from a checkpoint loader node your checkpoint does not contain a valid VAE.")

    def vae_encode_crop_pixels(self, pixels):
        if self.crop_input:
            downscale_ratio = self.spacial_compression_encode()

            dims = pixels.shape[1:-1]
            for d in range(len(dims)):
                x = (dims[d] // downscale_ratio) * downscale_ratio
                x_offset = (dims[d] % downscale_ratio) // 2
                if x != dims[d]:
                    pixels = pixels.narrow(d + 1, x_offset, x)

        if pixels.shape[-1] > self.output_channels:
            pixels = pixels[..., :self.output_channels]
        elif pixels.shape[-1] < self.output_channels:
            if self.pad_channel_value is not None:
                if isinstance(self.pad_channel_value, str):
                    mode = self.pad_channel_value

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Add a VAELoader node and point it at a standalone VAE file (e.g. sd_xl_vae.safetensors, vae-ft-mse-840000.safetensors) and connect it to the decode node.
  2. Use a full checkpoint that includes VAE weights instead of the pruned/unet-only file.
  3. Check the console for 'No VAE weights detected' to confirm the checkpoint lacks VAE keys.

Example fix

# before
# workflow: CheckpointLoaderSimple('flux-unet-only.safetensors') -> VAEDecode -> RuntimeError

# after
# CheckpointLoaderSimple('flux-unet-only.safetensors').MODEL/CLIP -> ...
# VAELoader('ae.safetensors').VAE -> VAEDecode.vae
Defensive patterns

Strategy: validation

Validate before calling

vae.throw_exception_if_invalid()  # cheap explicit check
# or: assert vae.first_stage_model is not None

Type guard

def vae_is_valid(vae) -> bool:
    return getattr(vae, 'first_stage_model', None) is not None

Try / catch

try:
    vae.throw_exception_if_invalid()
except RuntimeError as e:
    if 'VAE is invalid' in str(e):
        raise SystemExit('Checkpoint has no VAE; connect a VAELoader with a standalone VAE file.')
    raise

Prevention

When it happens

Trigger: Loading a checkpoint whose state dict contains no VAE keys (diffusion-model-only files, UNET-only safetensors, CLIP+model without VAE); passing a None VAE from CheckpointLoaderSimple into VAEDecode/VAEEncode; loading a Diffusers-format model split that excludes the VAE.

Common situations: Using unet-only or 'diffusion model' checkpoints in a workflow that still has VAEDecode wired; LoRA/pruned checkpoints where the VAE was stripped to save space; forgetting to add a separate VAELoader node.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/bff1465fe913b75e. Report an issue: GitHub.