invoke-ai/InvokeAI · error · NotAMatchError

standalone Qwen3-VL encoder directory does not contain model

Error message

standalone Qwen3-VL encoder directory does not contain model weights

What it means

After confirming the config is a valid Qwen3-VL 4B config, from_model_on_disk verifies that the weights directory actually contains complete pretrained weights: a single model.safetensors or pytorch_model.bin, or a sharded index whose every referenced shard file exists and is inside the folder. If not, it throws this NotAMatchError because an encoder directory without usable weights cannot be registered.

Source

Thrown at invokeai/backend/model_manager/configs/qwen3_vl_encoder.py:149

                "Qwen3VLModel",
                "Qwen3VLForConditionalGeneration",
            },
        )
        _validate_krea2_qwen3_vl_config(expected_config_path)

        if config_path_nested.exists():
            weights_path = mod.path / "text_encoder"
            tokenizer_path = mod.path / "tokenizer"
        else:
            weights_path = mod.path
            tokenizer_path = mod.path

        has_weights = _has_complete_pretrained_weights(weights_path)
        has_tokenizer = (tokenizer_path / "tokenizer.json").exists() or (
            (tokenizer_path / "vocab.json").exists() and (tokenizer_path / "merges.txt").exists()
        )
        if not has_weights:
            raise NotAMatchError("standalone Qwen3-VL encoder directory does not contain model weights")
        if not has_tokenizer:
            raise NotAMatchError("standalone Qwen3-VL encoder directory does not contain tokenizer files")

        return cls(**override_fields)


def _is_qwen3_vl_encoder_state_dict(state_dict: dict[str | int, Any]) -> bool:
    """True for a single-file Qwen3-VL encoder: a Qwen3 text decoder PLUS a visual tower.

    The visual tower (``visual.*`` / ``model.visual.*``) distinguishes Qwen3-VL from the text-only
    ``Qwen3Encoder`` (Z-Image / FLUX.2 Klein), which has ``model.layers.*`` but no visual tower.
    """
    str_keys = [k for k in state_dict if isinstance(k, str)]
    has_text_decoder = any(".layers." in k and ("model." in k or k.startswith("layers.")) for k in str_keys)
    has_visual_tower = any(k.startswith(("visual.", "model.visual.")) or ".visual." in k for k in str_keys)
    return has_text_decoder and has_visual_tower

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Re-download the model weights completely; for sharded models ensure every file listed in model.safetensors.index.json is present in the same folder.
  2. Verify shard filenames in the index's weight_map match the files on disk exactly (no renaming or path prefixes).
  3. If you assembled the folder by hand, copy the weight files (model.safetensors or pytorch_model.bin) next to config.json.
  4. Use a resumable downloader (huggingface-cli download) to repair partial downloads, then rescan the folder in InvokeAI.

Example fix

// before: sharded index with missing shard
models/qwen3vl-encoder/
  config.json
  model.safetensors.index.json
  (shards missing) -> NotAMatchError
// after
models/qwen3vl-encoder/
  config.json
  model.safetensors.index.json
  model-00001-of-00002.safetensors
  model-00002-of-00002.safetensors
Defensive patterns

Strategy: validation

Validate before calling

import json
from pathlib import Path

def has_complete_weights(model_dir: str) -> bool:
    p = Path(model_dir) / "text_encoder"
    if not p.is_dir():
        p = Path(model_dir)
    if (p / "model.safetensors").is_file() or (p / "pytorch_model.bin").is_file():
        return True
    for idx in ("model.safetensors.index.json", "pytorch_model.bin.index.json"):
        ip = p / idx
        if ip.is_file():
            wm = json.loads(ip.read_text()).get("weight_map", {})
            if not all((p / fn).is_file() for fn in wm.values()):
                return False
            return bool(wm)
    return False

Type guard

def weights_present(weights_dir) -> bool:
    from pathlib import Path
    p = Path(weights_dir)
    return (p / "model.safetensors").is_file() or (p / "pytorch_model.bin").is_file() or (p / "model.safetensors.index.json").is_file()

Try / catch

try:
    cfg = Qwen3VLEncoder_Qwen3VLEncoder_Config.from_model_on_disk(mod, {})
except NotAMatchError as e:
    if "does not contain model weights" in str(e):
        logger.warning("%s has no complete weights; re-run huggingface-cli download", mod.path)

Prevention

When it happens

Trigger: from_model_on_disk runs _has_complete_pretrained_weights on the weights path (text_encoder/ subfolder or directory root) and finds no model.safetensors/pytorch_model.bin and no valid complete sharded index - e.g. only an index json with missing shard files, or no weight files at all.

Common situations: Interrupted or partial HuggingFace download (index json present, shards missing), copying only config.json and tokenizer files, shards downloaded but renamed, shards placed outside the directory (absolute/escaping paths in weight_map), or storage cleanup deleting large safetensors shards.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/b1379ef4c9356a23. Report an issue: GitHub.