{"record":{"id":"2c869b7515249ab7","repo":"invoke-ai/InvokeAI","slug":"missing-text-encoder-subfolder","errorCode":null,"errorMessage":"missing text_encoder/ subfolder","messagePattern":"missing text_encoder/ subfolder","errorType":"validation","errorClass":"NotAMatchError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/model_manager/configs/qwen_vl_encoder.py","lineNumber":90,"sourceCode":"\n    @classmethod\n    def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:\n        raise_if_not_dir(mod)\n\n        raise_for_override_fields(cls, override_fields)\n\n        # Reject anything that looks like a full pipeline (those are matched as Main models).\n        if (mod.path / \"model_index.json\").exists() or (mod.path / \"transformer\").exists():\n            raise NotAMatchError(\n                \"directory looks like a full diffusers pipeline (has model_index.json or transformer folder), \"\n                \"not a standalone Qwen VL encoder\"\n            )\n\n        text_encoder_dir = mod.path / \"text_encoder\"\n        tokenizer_dir = mod.path / \"tokenizer\"\n\n        if not text_encoder_dir.is_dir():\n            raise NotAMatchError(\"missing text_encoder/ subfolder\")\n        if not tokenizer_dir.is_dir():\n            raise NotAMatchError(\"missing tokenizer/ subfolder\")\n\n        config_path = text_encoder_dir / \"config.json\"\n        if not config_path.is_file():\n            raise NotAMatchError(f\"missing {config_path}\")\n\n        try:\n            with open(config_path, \"r\", encoding=\"utf-8\") as f:\n                cfg = json.load(f)\n        except (OSError, json.JSONDecodeError) as e:\n            raise NotAMatchError(f\"could not read text_encoder/config.json: {e}\") from e\n\n        class_name = cfg.get(\"_class_name\")\n        architectures = cfg.get(\"architectures\") or []\n        candidates = {class_name, *architectures} - {None}\n\n        if not candidates & _RECOGNIZED_TEXT_ENCODER_CLASSES:","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/model_manager/configs/qwen_vl_encoder.py#L72-L108","documentation":"Raised as a NotAMatchError by QwenVLEncoder_Diffusers_Config.from_model_on_disk when the candidate directory lacks a text_encoder/ subfolder. The diffusers-style standalone Qwen2.5-VL encoder layout requires text_encoder/ (with config.json and weights) and tokenizer/ subfolders; without text_encoder/ there is nothing to classify, so the matcher rejects the directory.","triggerScenarios":"Importing a directory that is neither a full pipeline nor the expected layout — e.g. a folder containing only tokenizer/, only processor/, only a bare safetensors file at the root, or an empty/near-empty download directory — while the QwenVLEncoder_Diffusers matcher runs.","commonSituations":"Downloading only part of a HuggingFace repo; extracting an archive that nests files one level deeper than expected; manually renaming subfolders (e.g. 'text-encoder' or 'text_encoder' placed inside another folder); pointing at the repo root of an encoder repo whose weights live in a differently named subfolder.","solutions":["Verify the layout: the directory must contain text_encoder/ (with config.json + model.safetensors) and tokenizer/ subfolders.","Re-download with `huggingface-cli download <repo>` ensuring text_encoder/ and tokenizer/ are fetched, then rescan in InvokeAI.","Move or rename the subfolder to exactly `text_encoder` (check for typos and extra nesting levels).","If the model is a single .safetensors file, let it be matched by the QwenVLEncoder_Checkpoint config instead of forcing the diffusers-folder type."],"exampleFix":"// before (missing text_encoder/)\nmy-encoder/\n  tokenizer/\n  encoder_files/          # wrong name / nesting\n\n// after\nmy-encoder/\n  text_encoder/\n    config.json\n    model.safetensors\n  tokenizer/\n    tokenizer_config.json","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef has_standalone_qwenvl_layout(root: Path) -> bool:\n    return (\n        (root / \"text_encoder\").is_dir()\n        and (root / \"tokenizer\").is_dir()\n        and (root / \"text_encoder\" / \"config.json\").is_file()\n    )\n\nassert has_standalone_qwenvl_layout(Path(\"/path/to/model\")), \"expected text_encoder/ + tokenizer/ subfolders\"","typeGuard":"from pathlib import Path\n\ndef is_diffusers_encoder_layout(p: Path) -> bool:\n    te, tok = p / \"text_encoder\", p / \"tokenizer\"\n    return te.is_dir() and tok.is_dir() and (te / \"config.json\").is_file()","tryCatchPattern":"try:\n    invokeai_model_manager.probe(model_dir)\nexcept NotAMatchError as e:\n    if \"missing text_encoder/ subfolder\" in str(e):\n        fetch_missing_subfolders_from_hub(repo_id, needed=[\"text_encoder\", \"tokenizer\"], dest=model_dir)\n    else:\n        raise","preventionTips":["Download with huggingface-cli / snapshot_download so all subfolders arrive intact.","Verify exact subfolder names (text_encoder, tokenizer) — hyphens or extra nesting break matching.","Check the tree before import: text_encoder/config.json must be a file.","For single-file encoders, use the checkpoint (.safetensors) import path instead of a folder."],"tags":["invokeai","model-import","diffusers","directory-layout","not-a-match"],"backgroundTag":"missing-model-files","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}