invoke-ai/InvokeAI · error · ValueError

InpaintModelExt should be used only on inpaint models!

Error message

InpaintModelExt should be used only on inpaint models!

What it means

InpaintModelExt feeds the 9-channel inpainting UNet, so init_tensors asserts _is_inpaint_model(ctx.unet) (conv_in.in_channels == 9). Attaching it to a normal 4-channel UNet raises ValueError — the inverse check of InpaintExt.

Source

Thrown at invokeai/backend/stable_diffusion/extensions/inpaint_model.py:61

        if mask is not None:
            self._mask = 1 - mask
        self._masked_latents = masked_latents
        self._is_gradient_mask = is_gradient_mask

    @staticmethod
    def _is_inpaint_model(unet: UNet2DConditionModel):
        """Checks if the provided UNet belongs to a regular model.
        The `in_channels` of a UNet vary depending on model type:
        - normal - 4
        - depth - 5
        - inpaint - 9
        """
        return unet.conv_in.in_channels == 9

    @callback(ExtensionCallbackType.PRE_DENOISE_LOOP)
    def init_tensors(self, ctx: DenoiseContext):
        if not self._is_inpaint_model(ctx.unet):
            raise ValueError("InpaintModelExt should be used only on inpaint models!")

        if self._mask is None:
            self._mask = torch.ones_like(ctx.latents[:1, :1])
        self._mask = self._mask.to(device=ctx.latents.device, dtype=ctx.latents.dtype)

        if self._masked_latents is None:
            self._masked_latents = torch.zeros_like(ctx.latents[:1])
        self._masked_latents = self._masked_latents.to(device=ctx.latents.device, dtype=ctx.latents.dtype)

    # Do last so that other extensions works with normal latents
    @callback(ExtensionCallbackType.PRE_UNET, order=1000)
    def append_inpaint_layers(self, ctx: DenoiseContext):
        batch_size = ctx.unet_kwargs.sample.shape[0]
        b_mask = torch.cat([self._mask] * batch_size)
        b_masked_latents = torch.cat([self._masked_latents] * batch_size)
        ctx.unet_kwargs.sample = torch.cat(
            [ctx.unet_kwargs.sample, b_mask, b_masked_latents],
            dim=1,

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Use InpaintExt (for normal models) instead of InpaintModelExt when the UNet is not 9-channel.
  2. Load an actual inpainting checkpoint (e.g. SD-inpainting variant) for this workflow.
  3. Re-probe/re-add the model if it was mislabeled in the model manager.
  4. Check unet.conv_in.in_channels == 9 before attaching the extension in custom code.

Example fix

// before
extensions.append(InpaintModelExt(mask, masked_latents))  # normal unet
// after
if unet.conv_in.in_channels == 9:
    extensions.append(InpaintModelExt(mask, masked_latents))
else:
    extensions.append(InpaintExt(mask, masked_latents))
Defensive patterns

Strategy: validation

Validate before calling

if unet.conv_in.in_channels != 9:
    raise ValueError("InpaintModelExt requires an inpainting (9-channel) UNet")

Type guard

def is_inpaint_model(unet) -> bool:
    return unet.conv_in.in_channels == 9

Try / catch

try:
    result = pipeline(...)
except ValueError as e:
    if "InpaintModelExt should be used only on inpaint models" in str(e):
        result = run_with_inpaint_ext(pipeline, mask, masked_latents)
    else:
        raise

Prevention

When it happens

Trigger: A denoise graph registers InpaintModelExt while the loaded UNet is a standard (non-inpainting) model, so the callback fires with the wrong unet.

Common situations: User selecting an inpaint-style workflow against a regular SD checkpoint; model misconfigured/mislabeled as inpainting; custom extension wiring that doesn't check unet type.

Related errors


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