invoke-ai/InvokeAI · warning · NotAMatchError

state dict looks SDNQ-quantized; use Qwen3Encoder_SDNQ_Folde

Error message

state dict looks SDNQ-quantized; use Qwen3Encoder_SDNQ_Folder_Config

What it means

NotAMatchError raised by Qwen3Encoder_Qwen3Encoder_Config._reject_if_sdnq_quantized (qwen3_encoder.py:361). The fallback SDNQ check: even without a quantization_config.json, the state dict contains SDNQ-style weight+scale key pairs (_has_sdnq_keys), so the folder is SDNQ-quantized and must go to Qwen3Encoder_SDNQ_Folder_Config. Loading it via the unquantized loader would misread packed uint8 weights.

Source

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

    @classmethod
    def _reject_if_sdnq_quantized(cls, mod: ModelOnDisk) -> None:
        # Primary signal: quantization_config.json with quant_method="sdnq" (at root or in
        # text_encoder/). Fallback: SDNQ-style weight+scale key pairs in the state dict. This mirrors
        # the detection in Qwen3Encoder_SDNQ_Folder_Config so the two stay mutually exclusive.
        for folder in (mod.path, mod.path / "text_encoder"):
            quant_config_path = folder / "quantization_config.json"
            if not quant_config_path.exists():
                continue
            try:
                with open(quant_config_path, "r", encoding="utf-8") as f:
                    quant_config = json.load(f)
            except (json.JSONDecodeError, OSError):
                continue
            if quant_config.get("quant_method") == "sdnq":
                raise NotAMatchError("folder is SDNQ-quantized; use Qwen3Encoder_SDNQ_Folder_Config")

        if _has_sdnq_keys(mod.load_state_dict()):
            raise NotAMatchError("state dict looks SDNQ-quantized; use Qwen3Encoder_SDNQ_Folder_Config")

    @classmethod
    def _get_variant_from_config(cls, config_path) -> Qwen3VariantType:
        """Get variant from config.json based on hidden_size, or raise NotAMatch if unknown."""
        QWEN3_06B_HIDDEN_SIZE = 1024
        QWEN3_4B_HIDDEN_SIZE = 2560
        QWEN3_8B_HIDDEN_SIZE = 4096

        try:
            with open(config_path, "r", encoding="utf-8") as f:
                config = json.load(f)
        except (json.JSONDecodeError, OSError) as e:
            raise NotAMatchError(f"unable to read Qwen3 config.json: {e}") from e

        hidden_size = config.get("hidden_size")
        if hidden_size == QWEN3_8B_HIDDEN_SIZE:
            return Qwen3VariantType.Qwen3_8B
        elif hidden_size == QWEN3_4B_HIDDEN_SIZE:

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Let the Qwen3Encoder_SDNQ_Folder_Config match the model, or register it explicitly as SDNQ.
  2. Restore/re-add the quantization_config.json from the original SDNQ repo so detection uses the primary signal.
  3. Download the unquantized weights if you want the standard Qwen3Encoder loader.
  4. Inspect state-dict keys (look for *.weight_scale / packed uint8 tensors) to confirm the quantization before re-registering.

Example fix

// before
models/qwen3-encoder/  // model.safetensors has weight_scale tensors, no quantization_config.json
// after
models/qwen3-encoder/  // + quantization_config.json (quant_method: sdnq), registered via Qwen3Encoder_SDNQ_Folder_Config
Defensive patterns

Strategy: validation

Validate before calling

def has_sdnq_keys(state_dict: dict) -> bool:
    # SDNQ packs weights as uint8 with matching *.weight_scale tensors
    return any(k.endswith('.weight_scale') or k.endswith('_scale') for k in state_dict) and any(
        getattr(t, 'dtype', None) is not None and 'uint8' in str(t.dtype) for t in state_dict.values()
    )
# if True, register with Qwen3Encoder_SDNQ_Folder_Config

Type guard

def looks_sdnq_quantized(state_dict: dict) -> bool:
    scales = [k for k in state_dict if 'scale' in k.lower()]
    return len(scales) > 0 and any('uint8' in str(getattr(state_dict[k], 'dtype', '')).lower() for k in state_dict)

Try / catch

try:
    register_model(model_dir, model_type='Qwen3Encoder')
except NotAMatchError as e:
    if 'SDNQ' in str(e):
        register_model(model_dir, config='Qwen3Encoder_SDNQ_Folder_Config')
    else:
        raise

Prevention

When it happens

Trigger: from_model_on_disk where no sdnq quantization_config.json is present but _has_sdnq_keys(mod.load_state_dict()) finds weight+scale tensor pairs in the safetensors state dict.

Common situations: SDNQ conversions that omit quantization_config.json; hand-merged or re-uploaded checkpoints where the quant config file was dropped but quantized tensors remain; downloading weights only (config-only files excluded) from an SDNQ repo.

Related errors


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