invoke-ai/InvokeAI · error · TypeError

Expected Qwen3Encoder_SDNQ_Config or Qwen3Encoder_SDNQ_Folde

Error message

Expected Qwen3Encoder_SDNQ_Config or Qwen3Encoder_SDNQ_Folder_Config, got {type(config).__name__}.

What it means

_load_from_sdnq re-validates its config argument with a isinstance check and raises TypeError if the config is not Qwen3Encoder_SDNQ_Config or Qwen3Encoder_SDNQ_Folder_Config. This is a defensive guard reached when the dispatch in _load_model is bypassed (direct call) or the config type changed between dispatch and call.

Source

Thrown at invokeai/backend/model_manager/load/model_loaders/z_image.py:1501

        """Load tokenizer with local_files_only fallback for offline support."""
        try:
            return AutoTokenizer.from_pretrained(self.DEFAULT_TOKENIZER_SOURCE, local_files_only=True)
        except OSError:
            return AutoTokenizer.from_pretrained(self.DEFAULT_TOKENIZER_SOURCE)

    def _load_from_sdnq(
        self,
        config: AnyModelConfig,
    ) -> AnyModel:
        from transformers import Qwen3Config, Qwen3ForCausalLM

        from invokeai.backend.quantization.sdnq.sdnq_tensor import SDNQTensor
        from invokeai.backend.util.logging import InvokeAILogger

        logger = InvokeAILogger.get_logger(self.__class__.__name__)

        if not isinstance(config, (Qwen3Encoder_SDNQ_Config, Qwen3Encoder_SDNQ_Folder_Config)):
            raise TypeError(
                f"Expected Qwen3Encoder_SDNQ_Config or Qwen3Encoder_SDNQ_Folder_Config, got {type(config).__name__}."
            )
        model_path = Path(config.path)

        # Determine safe dtype based on target device capabilities
        target_device = TorchDevice.choose_torch_device()
        compute_dtype = TorchDevice.choose_bfloat16_safe_dtype(target_device)

        # Load the SDNQ state dict - this returns SDNQTensor wrappers (on CPU)
        sd = sdnq_sd_loader(model_path, compute_dtype=compute_dtype)

        # Determine Qwen model configuration from state dict
        layer_count = 0
        for key in sd.keys():
            if isinstance(key, str) and key.startswith("model.layers."):
                parts = key.split(".")
                if len(parts) > 2:
                    try:

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Pass a Qwen3Encoder_SDNQ_Config or Qwen3Encoder_SDNQ_Folder_Config instance (construct from the model path with the correct class).
  2. Do not call _load_from_sdnq directly; go through _load_model which performs the correct dispatch.
  3. Update any custom config classes to inherit from the supported SDNQ config types.
  4. Fix tests/mocks that replace the config with a generic object.

Example fix

// before
loader._load_from_sdnq(ModelConfigBase(path=p))
// after
cfg = Qwen3Encoder_SDNQ_Folder_Config(path=p)
loader._load_from_sdnq(cfg)
Defensive patterns

Strategy: type-guard

Validate before calling

cfg_t = type(config).__name__
assert cfg_t in ("Qwen3Encoder_SDNQ_Config", "Qwen3Encoder_SDNQ_Folder_Config"), \
    f"bad config for _load_from_sdnq: {cfg_t}"

Type guard

def is_sdnq_config(config: object) -> bool:
    return hasattr(config, "path") and type(config).__name__ in (
        "Qwen3Encoder_SDNQ_Config", "Qwen3Encoder_SDNQ_Folder_Config")

Try / catch

try:
    model = loader._load_model(cfg, SubModelType.TextEncoder)
except TypeError as e:
    if "Expected Qwen3Encoder_SDNQ" in str(e):
        cfg = rebuild_config_as_sdnq(cfg.path)
        model = loader._load_model(cfg, SubModelType.TextEncoder)
    else:
        raise

Prevention

When it happens

Trigger: Calling _load_from_sdnq directly with some other config object; refactored/forked code constructing configs that pass an earlier check but not this one; mocking in tests that substitutes a generic config.

Common situations: Plugin or custom-node code calling the loader's private method directly; subclasses overriding _load_model without preserving the config contract; stale pickled/serialized config objects of an older class version.

Related errors


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