invoke-ai/InvokeAI · info · NotAMatchError
directory looks like a complete causal LM (config.json and t
Error message
directory looks like a complete causal LM (config.json and tokenizer files at root), not a standalone Qwen3 encoder
What it means
NotAMatchError raised in Qwen3Encoder_Qwen3Encoder_Config.from_model_on_disk (qwen3_encoder.py:318). The directory has config.json at root plus tokenizer files (tokenizer.json, tokenizer.model, or tokenizer_config.json). A standalone Qwen3 text-encoder download never bundles tokenizer files; their presence indicates a complete causal LM (TextLLM), so the config rejects the folder so the TextLLM config can match it.
Source
Thrown at invokeai/backend/model_manager/configs/qwen3_encoder.py:318
raise NotAMatchError(
"directory looks like a full diffusers pipeline (has model_index.json or transformer folder), "
"not a standalone Qwen3 encoder"
)
# Check for text_encoder config - support both:
# 1. Full model structure: model_root/text_encoder/config.json
# 2. Standalone text_encoder download: model_root/config.json (when text_encoder subfolder is downloaded separately)
config_path_nested = mod.path / "text_encoder" / "config.json"
config_path_direct = mod.path / "config.json"
if config_path_nested.exists():
expected_config_path = config_path_nested
elif config_path_direct.exists():
# Standalone text_encoder downloads do not bundle tokenizer files. If we see tokenizer files at the
# root next to config.json, this is a complete causal LM (TextLLM), not a Qwen3 encoder subfolder.
tokenizer_files = ("tokenizer.json", "tokenizer.model", "tokenizer_config.json")
if any((mod.path / f).exists() for f in tokenizer_files):
raise NotAMatchError(
"directory looks like a complete causal LM (config.json and tokenizer files at root), "
"not a standalone Qwen3 encoder"
)
expected_config_path = config_path_direct
else:
raise NotAMatchError(
f"unable to load config file(s): {{PosixPath('{config_path_nested}'): 'file does not exist'}}"
)
# Qwen3 uses Qwen2VLForConditionalGeneration or similar
raise_for_class_name(expected_config_path, _QWEN3_ENCODER_ARCHITECTURES)
# Reject SDNQ-quantized encoders so Qwen3Encoder_SDNQ_Folder_Config matches them instead.
# A real SDNQ Qwen3 encoder has the same Qwen3 config class name as an unquantized one, so
# without this guard both configs accept the folder — and since they share the Qwen3Encoder
# type, the factory tiebreak is non-deterministic. If it picked this (unquantized) config,
# the non-SDNQ loader would then mis-read the packed uint8 weights.
cls._reject_if_sdnq_quantized(mod)View on GitHub (pinned to 0b6a024f2f)
Solutions
- Install the folder as a TextLLM / main model — that is what it is.
- If you need a Qwen3 encoder, download only the text_encoder subfolder contents (config.json + model weights, no tokenizer) into a dedicated directory.
- If the tokenizer files are accidental leftovers, remove them and rescan.
- Place the complete LM outside the directory the encoder scanner walks.
Example fix
// before models/qwen3-encoder/ // config.json + tokenizer.json + safetensors (a full LM) // after models/qwen3-textllm/ // full LM, registered as TextLLM models/qwen3-encoder/ // only config.json + model.safetensors
Defensive patterns
Strategy: validation
Validate before calling
def is_complete_causal_lm(path) -> bool:
if not (path / 'config.json').exists():
return False
return any((path / f).exists() for f in ('tokenizer.json', 'tokenizer.model', 'tokenizer_config.json')) Type guard
def is_standalone_qwen3_encoder_dir(path) -> bool:
has_cfg = (path / 'config.json').exists() or (path / 'text_encoder' / 'config.json').exists()
has_tokenizer = any((path / f).exists() for f in ('tokenizer.json', 'tokenizer.model', 'tokenizer_config.json'))
return has_cfg and not has_tokenizer Try / catch
if is_complete_causal_lm(model_dir):
register_model(model_dir, model_type='TextLLM')
else:
try:
register_model(model_dir, model_type='Qwen3Encoder')
except NotAMatchError as e:
logger.warning('Not a standalone encoder: %s', e) Prevention
- Exclude tokenizer files when downloading only encoder weights (e.g. allow_patterns=['config.json','*.safetensors']).
- Register full Qwen3 LM repos as TextLLM, not encoders.
- Keep tokenizer assets in a separate folder from encoder-only downloads.
When it happens
Trigger: from_model_on_disk scanning a directory where mod.path/config.json exists AND any of tokenizer.json / tokenizer.model / tokenizer_config.json exists at the root.
Common situations: Downloading a full Qwen3-4B/8B causal LM repo (which always ships tokenizer files) and expecting it to register as a Qwen3 text encoder; confusing Qwen3ForCausalLM checkpoints with the Z-Image text_encoder subfolder.
Related errors
- directory looks like a full diffusers pipeline (has model_in
- Expected PreTrainedTokenizerBase for tokenizer, got {type(to
- Expected PreTrainedTokenizerBase for tokenizer, got {type(to
- hidden size does not match a known Qwen3 variant
- state dict does not look like a Qwen3 model
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/764c99053d6c2de6.
Report an issue: GitHub.