invoke-ai/InvokeAI · error · NotAMatchError
standalone Qwen3-VL encoder directory does not contain…
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.
Solutions
- Re-download the model weights completely; for sharded models ensure every file listed in model.safetensors.index.json is present in the same folder.
- Verify shard filenames in the index's weight_map match the files on disk exactly (no renaming or path prefixes).
- If you assembled the folder by hand, copy the weight files (model.safetensors or pytorch_model.bin) next to config.json.
- 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
- Use resumable downloaders (huggingface-cli download / snapshot_download) instead of manual per-file fetches.
- For sharded models, verify every weight_map entry exists before importing.
- Do not delete large safetensors shards to free space without removing the model from InvokeAI.
- Keep shard files next to their index and unrenamed.
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
- unable to load config file
- directory looks like a full diffusers pipeline (has…
- directory looks like a full diffusers pipeline (has…
- expected a .safetensors file, got
- missing text_encoder/ subfolder
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/b1379ef4c9356a23.
Report an issue: GitHub.
Appendix: 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)