invoke-ai/InvokeAI · warning · NotAMatchError

state dict does not look like a Qwen3 model

Error message

state dict does not look like a Qwen3 model

What it means

NotAMatchError raised by Qwen3Encoder._validate_looks_like_qwen3_model (qwen3_encoder.py:233) during model identification via from_model_on_disk. The state dict loaded from the candidate model folder lacks the keys that identify a Qwen3 model (e.g. token_embd.weight / blk.* for GGUF or Qwen3 transformer keys), so this config claims it is not a Qwen3 encoder and lets other configs match. It is a classification guard, not a fatal failure of your model.

Source

Thrown at invokeai/backend/model_manager/configs/qwen3_encoder.py:233

    @classmethod
    def _get_variant_or_default(cls, mod: ModelOnDisk) -> Qwen3VariantType:
        """Get the variant from state dict, raising NotAMatch when the size does not match a known Qwen3 variant.

        We previously defaulted to 4B for unknown sizes, but that swallowed other causal-LM GGUFs
        (Mistral, Llama, ...) which share llama.cpp tensor naming with Qwen3.
        """
        state_dict = mod.load_state_dict()
        variant = _get_qwen3_variant_from_state_dict(state_dict)
        if variant is None:
            raise NotAMatchError("hidden size does not match a known Qwen3 variant")
        return variant

    @classmethod
    def _validate_looks_like_qwen3_model(cls, mod: ModelOnDisk) -> None:
        state_dict = mod.load_state_dict()
        if not _has_qwen3_keys(state_dict):
            raise NotAMatchError("state dict does not look like a Qwen3 model")
        # Reject T5 encoders: they share the token_embd.weight key with Qwen3 GGUFs but use the ``enc.``
        # block prefix, and must be classified as T5Encoder (Qwen3 encoders never have ``enc.blk.*`` keys).
        if _has_t5_encoder_keys(state_dict):
            raise NotAMatchError("state dict looks like a T5 encoder (has 'enc.blk.*' keys), not a Qwen3 encoder")
        # Reject Gemma-2/3 encoders: their GGUFs also carry token_embd.weight + blk.* keys but use
        # post-attention / post-feedforward norms a Qwen3 encoder never has; they must be classified as
        # Gemma2Encoder (otherwise a Gemma GGUF matches both configs and can be re-identified wrongly).
        if _has_gemma2_keys(state_dict):
            raise NotAMatchError(
                "state dict looks like a Gemma-2 encoder (has post_attention_norm/post_ffw_norm keys), "
                "not a Qwen3 encoder"
            )
        # Reject Qwen2.5-VL / Qwen2-VL encoders: they carry a visual tower and must be
        # classified as QwenVLEncoder (text-only Qwen3 encoders never have one).
        if _has_qwen_vl_visual_tower(state_dict):
            raise NotAMatchError(
                "state dict bundles a Qwen-VL visual tower; this is a Qwen-VL encoder, not a text-only Qwen3 encoder"
            )

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Verify the model actually is a Qwen3 encoder (check config.json architectures or GGUF metadata); if not, install it under the correct model type/config.
  2. Re-download the model — missing tensors mean an incomplete snapshot; confirm all safetensors/GGUF shards are present.
  3. If it is a genuine Qwen3 encoder, ensure the state dict key names are standard (blk.*, token_embd.weight); re-convert with a current llama.cpp/convert script if keys were renamed.
  4. Scan the correct folder: the config expects model_root/text_encoder/config.json or model_root/config.json with the weights alongside.
  5. If identification should not be attempted for this file, exclude it from the scan directory or add an explicit type/override fields.

Example fix

// before (wrong folder scanned)
models/qwen3-encoder/  # contains unrelated Llama GGUF
// after
models/qwen3-encoder/text_encoder/token_embd.weight etc. (real Qwen3 tensors),
models/llama/ (install as its own model type)
Defensive patterns

Strategy: validation

Validate before calling

from safetensors import safe_open
import json, pathlib
def looks_like_qwen3_encoder(path: pathlib.Path) -> bool:
    cfg = None
    for cand in (path / 'text_encoder' / 'config.json', path / 'config.json'):
        if cand.exists():
            cfg = json.loads(cand.read_text())
            break
    if cfg is None:
        return False
    archs = cfg.get('architectures', [])
    return any(a in {'Qwen3ForCausalLM', 'Qwen2ForCausalLM', 'Qwen2VLForConditionalGeneration'} for a in archs)

Type guard

def is_qwen3_state_dict(state_dict: dict) -> bool:
    keys = set(state_dict)
    has_qwen3 = 'token_embd.weight' in keys or any(k.startswith('blk.') or k.startswith('model.layers.') for k in keys)
    not_t5 = not any(k.startswith('enc.blk.') for k in keys)
    return has_qwen3 and not_t5

Try / catch

from invokeai.backend.model_manager.configs.qwen3_encoder import Qwen3Encoder_Qwen3Encoder_Config
from invokeai.backend.model_manager.configs.base import NotAMatchError
try:
    cfg = Qwen3Encoder_Qwen3Encoder_Config.from_model_on_disk(mod, overrides)
except NotAMatchError as e:
    logger.warning('Not a Qwen3 encoder: %s — trying other configs', e)
    cfg = fallback_identify(mod, overrides)

Prevention

When it happens

Trigger: Calling ModelManager search/heuristic model install (Qwen3Encoder config's from_model_on_disk) on a folder whose state dict does not contain _has_qwen3_keys() matches — e.g. a safetensors folder of a non-Qwen model, a weights file missing blk/token_embd tensors, or a GGUF whose tensors were renamed/stripped.

Common situations: Installing a Llama/Mistral/T5 GGUF or safetensors folder into InvokeAI; downloading an incomplete/partial model snapshot (missing weight shards); pointing the scan at the wrong subdirectory so load_state_dict() sees unrelated files.

Related errors


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