invoke-ai/InvokeAI · warning · NotAMatchError

unable to load config file(s): {{PosixPath('{config_path_nes

Error message

unable to load config file(s): {{PosixPath('{config_path_nested}'): 'file does not exist'}}

What it means

NotAMatchError raised in Qwen3Encoder_Qwen3Encoder_Config.from_model_on_disk (qwen3_encoder.py:324) when neither text_encoder/config.json nor config.json exists in the scanned directory. The message is phrased as a config-load failure naming the nested path, but its meaning is simply: this directory does not contain a Qwen3 encoder config.json, so this config does not match.

Source

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

        # 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)

        # Determine variant from config.json hidden_size
        variant = cls._get_variant_from_config(expected_config_path)

        return cls(variant=variant, **override_fields)

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Ensure the directory contains config.json either at the root or under text_encoder/; re-download it from the model repo.
  2. Check for .no_exist / partial-download markers from huggingface_hub and retry the download.
  3. If this is a GGUF model, it will never have config.json here — install it via the GGUF flow instead.
  4. Point the scanner at the folder that actually holds the encoder config.

Example fix

// before
models/qwen3-encoder/  // only model.safetensors
// after
models/qwen3-encoder/  // config.json + model.safetensors (config.json re-downloaded from the repo)
Defensive patterns

Strategy: validation

Validate before calling

def has_encoder_config(path) -> bool:
    return (path / 'text_encoder' / 'config.json').exists() or (path / 'config.json').exists()
# call before install; if False, re-download config.json from the model repo

Type guard

def encoder_config_path(path):
    nested = path / 'text_encoder' / 'config.json'
    direct = path / 'config.json'
    if nested.exists():
        return nested
    if direct.exists():
        return direct
    return None  # None means the Qwen3Encoder config will reject it

Try / catch

cfg = encoder_config_path(model_dir)
if cfg is None:
    raise FileNotFoundError(f'No config.json under {model_dir}; re-download from the model repo')
try:
    register_model(model_dir, model_type='Qwen3Encoder')
except NotAMatchError as e:
    logger.warning('Qwen3Encoder config rejected folder: %s', e)

Prevention

When it happens

Trigger: from_model_on_disk on a directory where both mod.path/'text_encoder'/'config.json' and mod.path/'config.json' are missing — e.g. an empty folder, a folder with only weights and no config, or weights placed at the wrong nesting level.

Common situations: Interrupted HuggingFace downloads that skipped config.json; copying only *.safetensors files; scanning a GGUF directory (no config.json at all); pointing the scan at the model repo root when config.json lives in a differently named subfolder.

Related errors


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