{"record":{"id":"92b4041770643824","repo":"invoke-ai/InvokeAI","slug":"inpaintext-should-be-used-only-on-normal-non-inpa","errorCode":null,"errorMessage":"InpaintExt should be used only on normal (non-inpainting) models. This could be caused by an inpainting model that was incorrectly marked as a non-inpainting model. In some cases, this can be fixed by removing and re-adding the model (so that it gets re-probed).","messagePattern":"InpaintExt should be used only on normal \\(non-inpainting\\) models\\. This could be caused by an inpainting model that was incorrectly marked as a non-inpainting model\\. In some cases, this can be fixed by removing and re-adding the model \\(so that it gets re-probed\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/stable_diffusion/extensions/inpaint.py","lineNumber":78,"sourceCode":"            t = einops.repeat(t, \"-> batch\", batch=batch_size)\n        # Noise shouldn't be re-randomized between steps here. The multistep schedulers\n        # get very confused about what is happening from step to step when we do that.\n        mask_latents = ctx.scheduler.add_noise(ctx.inputs.orig_latents, self._noise, t)\n        # TODO: Do we need to also apply scheduler.scale_model_input? Or is add_noise appropriately scaled already?\n        # mask_latents = self.scheduler.scale_model_input(mask_latents, t)\n        mask_latents = einops.repeat(mask_latents, \"b c h w -> (repeat b) c h w\", repeat=batch_size)\n        if self._is_gradient_mask:\n            threshold = (t.item()) / ctx.scheduler.config.num_train_timesteps\n            mask_bool = mask < 1 - threshold\n            masked_input = torch.where(mask_bool, latents, mask_latents)\n        else:\n            masked_input = torch.lerp(latents, mask_latents.to(dtype=latents.dtype), mask.to(dtype=latents.dtype))\n        return masked_input\n\n    @callback(ExtensionCallbackType.PRE_DENOISE_LOOP)\n    def init_tensors(self, ctx: DenoiseContext):\n        if not self._is_normal_model(ctx.unet):\n            raise ValueError(\n                \"InpaintExt should be used only on normal (non-inpainting) models. This could be caused by an \"\n                \"inpainting model that was incorrectly marked as a non-inpainting model. In some cases, this can be \"\n                \"fixed by removing and re-adding the model (so that it gets re-probed).\"\n            )\n\n        self._mask = self._mask.to(device=ctx.latents.device, dtype=ctx.latents.dtype)\n\n        self._noise = ctx.inputs.noise\n        # 'noise' might be None if the latents have already been noised (e.g. when running the SDXL refiner).\n        # We still need noise for inpainting, so we generate it from the seed here.\n        if self._noise is None:\n            self._noise = torch.randn(\n                ctx.latents.shape,\n                dtype=torch.float32,\n                device=\"cpu\",\n                generator=torch.Generator(device=\"cpu\").manual_seed(ctx.seed),\n            ).to(device=ctx.latents.device, dtype=ctx.latents.dtype)\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/stable_diffusion/extensions/inpaint.py#L60-L96","documentation":"InpaintExt is the extension for adding mask/source latents to NORMAL (4-channel) UNets by lerping them into the latents. init_tensors asserts the loaded UNet is not an inpainting model (9-channel); if it is, the wrong extension was attached and ValueError is raised. The message also flags likely model-probe misclassification.","triggerScenarios":"A denoise graph attaches InpaintExt while ctx.unet is an inpainting model, usually because the model was incorrectly probed/registered as a non-inpainting checkpoint.","commonSituations":"Model manager misdetecting an inpainting checkpoint's variant; user selecting the wrong pipeline type in the UI; stale model records after files were replaced with different variants.","solutions":["Remove and re-add the model in the model manager so it is re-probed with the correct variant.","Verify the checkpoint really is a non-inpainting model and choose the matching inpaint pipeline/extension instead (InpaintModelExt).","Update InvokeAI in case the variant-probing bug is fixed upstream.","Check is_inpainting_model(unet) (conv_in.in_channels == 9) before attaching InpaintExt in custom code."],"exampleFix":"// before\nextensions.append(InpaintExt(mask, masked_latents))  # unet is inpainting model\n// after\nif unet.conv_in.in_channels == 9:\n    extensions.append(InpaintModelExt(mask, masked_latents))\nelse:\n    extensions.append(InpaintExt(mask, masked_latents))","handlingStrategy":"validation","validationCode":"if unet.conv_in.in_channels == 9:\n    raise ValueError(\"InpaintExt cannot be used with an inpainting (9-channel) UNet\")","typeGuard":"def is_normal_model(unet) -> bool:\n    return unet.conv_in.in_channels != 9","tryCatchPattern":"try:\n    result = pipeline(...)\nexcept ValueError as e:\n    if \"InpaintExt should be used only on normal\" in str(e):\n        result = run_with_inpaint_model_ext(pipeline, mask, masked_latents)\n    else:\n        raise","preventionTips":["Check UNet in_channels before choosing inpaint extension","Re-add misdetected models in the model manager to re-probe variant","Match extension (InpaintExt vs InpaintModelExt) to checkpoint type","Keep InvokeAI updated for variant-probing fixes"],"tags":["python","valueerror","inpainting","model-probing"],"backgroundTag":"wrong-inpaint-extension","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}