invoke-ai/InvokeAI · error · ValueError

Unexpected submodel requested for Spandrel model.

Error message

Unexpected submodel requested for Spandrel model.

What it means

Spandrel image-to-image models are single-file upscalers/restoration networks with no submodels. The loader raises this ValueError if any submodel_type is supplied. It is a contract guard, similar to the SigLIP and TI loaders.

Source

Thrown at invokeai/backend/model_manager/load/model_loaders/spandrel_image_to_image.py:25

from invokeai.backend.model_manager.load.load_default import ModelLoader
from invokeai.backend.model_manager.load.model_loader_registry import ModelLoaderRegistry
from invokeai.backend.model_manager.taxonomy import AnyModel, BaseModelType, ModelFormat, ModelType, SubModelType
from invokeai.backend.spandrel_image_to_image_model import SpandrelImageToImageModel


@ModelLoaderRegistry.register(
    base=BaseModelType.Any, type=ModelType.SpandrelImageToImage, format=ModelFormat.Checkpoint
)
class SpandrelImageToImageModelLoader(ModelLoader):
    """Class for loading Spandrel Image-to-Image models (i.e. models wrapped by spandrel.ImageModelDescriptor)."""

    def _load_model(
        self,
        config: AnyModelConfig,
        submodel_type: Optional[SubModelType] = None,
    ) -> AnyModel:
        if submodel_type is not None:
            raise ValueError("Unexpected submodel requested for Spandrel model.")

        model_path = Path(config.path)
        model = SpandrelImageToImageModel.load_from_file(model_path)

        torch_dtype = self._torch_dtype
        if not model.supports_dtype(torch_dtype):
            self._logger.warning(
                f"The configured dtype ('{self._torch_dtype}') is not supported by the {model.get_model_type_name()} "
                "model. Falling back to 'float32'."
            )
            torch_dtype = torch.float32
        model.to(dtype=torch_dtype)

        return model

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Pass submodel_type=None when loading Spandrel models.
  2. Route diffusion-submodel requests to the appropriate main-model loader instead.
  3. Guard the call site by checking the model type before supplying a submodel_type.

Example fix

// before
model = loader.load_model(config, submodel_type=SubModelType.VAE)
// after
model = loader.load_model(config, submodel_type=None)
Defensive patterns

Strategy: validation

Validate before calling

if model_type is ModelType.Spandrel and submodel_type is not None:
    submodel_type = None  # Spandrel models are loaded whole

Type guard

def is_whole_model_type(model_type: ModelType) -> bool:
    return model_type in {ModelType.Spandrel, ModelType.SigLIP, ModelType.TextualInversion, ModelType.TextLLM}

Try / catch

try:
    model = loader.load_model(config, submodel_type=None)
except ValueError as e:
    logger.error("Spandrel load failed: %s", e)
    raise

Prevention

When it happens

Trigger: Calling load_model on a Spandrel model config with submodel_type set (e.g. SubModelType.Transformer or VAE) instead of None.

Common situations: Generic loading code that always passes a submodel_type because it was written for main diffusion pipelines; misconfigured pipeline code routing an upscale request through submodel logic.

Related errors


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