{"record":{"id":"2049bdeea3cde9a4","repo":"invoke-ai/InvokeAI","slug":"unable-to-read-qwen3-config-json-e","errorCode":null,"errorMessage":"unable to read Qwen3 config.json: {e}","messagePattern":"unable to read Qwen3 config\\.json: (.+?)","errorType":"exception","errorClass":"NotAMatchError","httpStatus":null,"severity":"warning","filePath":"invokeai/backend/model_manager/configs/qwen3_encoder.py","lineNumber":374,"sourceCode":"                continue\n            if quant_config.get(\"quant_method\") == \"sdnq\":\n                raise NotAMatchError(\"folder is SDNQ-quantized; use Qwen3Encoder_SDNQ_Folder_Config\")\n\n        if _has_sdnq_keys(mod.load_state_dict()):\n            raise NotAMatchError(\"state dict looks SDNQ-quantized; use Qwen3Encoder_SDNQ_Folder_Config\")\n\n    @classmethod\n    def _get_variant_from_config(cls, config_path) -> Qwen3VariantType:\n        \"\"\"Get variant from config.json based on hidden_size, or raise NotAMatch if unknown.\"\"\"\n        QWEN3_06B_HIDDEN_SIZE = 1024\n        QWEN3_4B_HIDDEN_SIZE = 2560\n        QWEN3_8B_HIDDEN_SIZE = 4096\n\n        try:\n            with open(config_path, \"r\", encoding=\"utf-8\") as f:\n                config = json.load(f)\n        except (json.JSONDecodeError, OSError) as e:\n            raise NotAMatchError(f\"unable to read Qwen3 config.json: {e}\") from e\n\n        hidden_size = config.get(\"hidden_size\")\n        if hidden_size == QWEN3_8B_HIDDEN_SIZE:\n            return Qwen3VariantType.Qwen3_8B\n        elif hidden_size == QWEN3_4B_HIDDEN_SIZE:\n            return Qwen3VariantType.Qwen3_4B\n        elif hidden_size == QWEN3_06B_HIDDEN_SIZE:\n            return Qwen3VariantType.Qwen3_06B\n        raise NotAMatchError(f\"hidden_size {hidden_size} does not match a known Qwen3 variant\")\n\n\nclass Qwen3Encoder_GGUF_Config(Checkpoint_Config_Base, Config_Base):\n    \"\"\"Configuration for GGUF-quantized Qwen3 Encoder models.\"\"\"\n\n    base: Literal[BaseModelType.Any] = Field(default=BaseModelType.Any)\n    type: Literal[ModelType.Qwen3Encoder] = Field(default=ModelType.Qwen3Encoder)\n    format: Literal[ModelFormat.GGUFQuantized] = Field(default=ModelFormat.GGUFQuantized)\n    cpu_only: bool | None = Field(default=None, description=\"Whether this model should run on CPU only\")","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/model_manager/configs/qwen3_encoder.py#L356-L392","documentation":"During model identification, Qwen3Encoder config classes read the model's config.json to determine which Qwen3 variant (8B/4B/0.6B) the weights belong to. If the file cannot be parsed as JSON or cannot be opened on disk, _get_variant_from_config wraps the underlying OSError/JSONDecodeError in a NotAMatchError so the model-record store can skip this config and try the next candidate. It is a normal 'not this format' signal, not a crash of the library.","triggerScenarios":"Calling ModelManager install/scan (from_model_on_disk -> _get_variant_from_config) on a folder containing a config.json that is truncated, corrupted, mid-download, has invalid JSON syntax, or is unreadable due to file permissions/encoding, while the folder otherwise looks like a Qwen3 text-encoder model.","commonSituations":"Interrupted HuggingFace downloads leaving a partial config.json; manually edited config.json with a trailing comma or comment; symlink to a missing file; permission-denied file on shared/NFS mounts; a non-Qwen3 repo that happens to be probed by this config class.","solutions":["Re-download the model (or just config.json) from the source repo so the file is complete and valid JSON.","Validate the file: `python -c \"import json;json.load(open('<path>/config.json'))\"` and fix any syntax errors reported.","Check file permissions/ownership on config.json and the parent directory (chmod u+r / chown).","If the model is not actually a Qwen3 encoder, ignore the NotAMatchError — it is expected behavior and other config classes will claim the model.","If the file is intentionally absent, ensure the model layout matches what InvokeAI expects (config.json present at the model root)."],"exampleFix":"// before (corrupt/truncated config.json)\n{\"architectures\": [\"Qwen3ForCausalLM\"], \"hidden_size\": 4096\n// after (complete valid JSON)\n{\"architectures\": [\"Qwen3ForCausalLM\"], \"hidden_size\": 4096}\n","handlingStrategy":"validation","validationCode":"import json, os\n\ndef qwen3_config_is_readable(model_dir):\n    p = os.path.join(model_dir, 'config.json')\n    if not os.path.isfile(p):\n        return False\n    try:\n        with open(p, 'r', encoding='utf-8') as f:\n            cfg = json.load(f)\n    except (json.JSONDecodeError, OSError):\n        return False\n    return isinstance(cfg.get('hidden_size'), int)","typeGuard":"def has_valid_qwen3_config(cfg) -> bool:\n    return isinstance(cfg, dict) and isinstance(cfg.get('hidden_size'), int)","tryCatchPattern":"from invokeai.backend.model_manager.configs.qwen3_encoder import Qwen3Encoder_Checkpoint_Config\n\n\ntry:\n    config = Qwen3Encoder_Checkpoint_Config.from_model_on_disk(mod, subtype)\nexcept NotAMatchError as e:\n    logger.warning(f'Skipping {mod.path}: {e}')  # expected for non-Qwen3 / corrupt config.json","preventionTips":["Always download models with a resumable tool (huggingface-cli/hf download) so config.json is never partial.","Run `json.load()` on config.json as a pre-install sanity check.","Never hand-edit config.json with trailing commas or comments.","Check file read permissions before scanning large model directories."],"tags":["model-identification","json-parse","invokeai"],"backgroundTag":"invalid-json-config","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}