invoke-ai/InvokeAI · info · NotAMatchError

no tokenizer_2 folder resolvable for this SDNQ T5 encoder la

Error message

no tokenizer_2 folder resolvable for this SDNQ T5 encoder layout

What it means

NotAMatchError from T5Encoder_SDNQ_Config.from_model_on_disk. Every FLUX workflow needs a Tokenizer2 that the loader can only load from a `tokenizer_2/` folder next to the encoder. An SDNQ T5 install with no resolvable tokenizer_2 directory is rejected at identification time so it never registers as a selectable T5 that would fail mid-workflow.

Source

Thrown at invokeai/backend/model_manager/configs/t5_encoder.py:137

    cpu_only: bool | None = Field(default=None, description="Whether this model should run on CPU only")

    @classmethod
    def from_model_on_disk(cls, mod: ModelOnDisk, override_fields: dict[str, Any]) -> Self:
        raise_if_not_dir(mod)

        raise_for_override_fields(cls, override_fields)

        te_dir = cls._locate_text_encoder_dir(mod)
        raise_for_class_name(te_dir / "config.json", "T5EncoderModel")

        cls._raise_if_not_sdnq_quantized(te_dir)

        # Every FLUX workflow requests a Tokenizer2 alongside the encoder, and the loader can only load
        # it from a `tokenizer_2/` folder. Reject an install that has no resolvable tokenizer (e.g. a
        # bare inline `text_encoder_2` folder with no sibling `tokenizer_2/`) at identification time so
        # it never registers as a selectable T5 that then fails mid-workflow on the missing tokenizer.
        if cls.resolve_tokenizer_dir(mod.path) is None:
            raise NotAMatchError("no tokenizer_2 folder resolvable for this SDNQ T5 encoder layout")

        return cls(**override_fields)

    @staticmethod
    def resolve_text_encoder_dir(path: Path) -> Optional[Path]:
        """Return the directory holding T5's config.json + safetensors, or None.

        Two layouts: a standalone bundle (``path`` is the pipeline root, T5 under ``text_encoder_2/``)
        or an inline submodel (``path`` *is* the ``text_encoder_2`` folder).
        """
        nested = path / "text_encoder_2"
        if (nested / "config.json").exists():
            return nested
        if (path / "config.json").exists():
            return path
        return None

    @staticmethod

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Copy/download the `tokenizer_2/` folder from the source FLUX repo as a sibling of the encoder dir
  2. Re-download the complete model repo including tokenizer_2 files
  3. If the model is not for FLUX workflows, register it under a different config/model type explicitly

Example fix

// before
mymodel/
  text_encoder_2/...
// after
mymodel/
  text_encoder_2/...
  tokenizer_2/
    tokenizer.json
    tokenizer_config.json
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
def has_tokenizer2(model_dir: Path) -> bool:
    return (model_dir / "tokenizer_2").is_dir() and any((model_dir / "tokenizer_2").iterdir())

Try / catch

try:
    install_model(path)
except NotAMatchError as e:
    if "tokenizer_2" in str(e):
        logger.error("Download tokenizer_2/ from the FLUX repo before installing")

Prevention

When it happens

Trigger: from_model_on_disk on an SDNQ T5 layout where cls.resolve_tokenizer_dir(mod.path) returns None — e.g. a bare inline `text_encoder_2/` folder with no sibling `tokenizer_2/`, or tokenizer files named/moved elsewhere.

Common situations: Downloading only the text encoder from a FLUX repo without tokenizer_2, moving tokenizer files out of the folder, or building a minimal encoder-only install for non-FLUX use.

Related errors


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