{"record":{"id":"44f7df01b5b2071c","repo":"invoke-ai/InvokeAI","slug":"the-denoising-window-denoising-start-denoisin","errorCode":null,"errorMessage":"The denoising window [{denoising_start}, {denoising_end}] rounds to zero steps at steps={num_steps}. Increase steps or widen the window.","messagePattern":"The denoising window \\[(.+?), (.+?)\\] rounds to zero steps at steps=(.+?)\\. Increase steps or widen the window\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/ernie_image/sampling_utils.py","lineNumber":75,"sourceCode":"    for i, t in enumerate(normalized):\n        text_bth[i, : t.shape[0], :] = t\n    return text_bth, lens\n\n\ndef get_schedule(num_steps: int, denoising_start: float = 0.0, denoising_end: float = 1.0) -> torch.Tensor:\n    \"\"\"Linear sigma schedule from 1.0 -> 0.0, same convention as the upstream pipeline.\"\"\"\n    if not 0.0 <= denoising_start < denoising_end <= 1.0:\n        raise ValueError(f\"Invalid denoising window: start={denoising_start}, end={denoising_end}\")\n    sigmas = torch.linspace(1.0, 0.0, num_steps + 1)\n    start = int(num_steps * denoising_start)\n    end = int(num_steps * denoising_end)\n    # Slice to [start, end] inclusive of both ends so the caller can use adjacent pairs.\n    window = sigmas[start : end + 1]\n    if window.numel() < 2:\n        # A window that rounds down to a single sigma yields zero adjacent pairs, i.e. zero steps.\n        # The denoise loop would then return its input untouched and the graph would decode raw\n        # noise with no error, so refuse instead.\n        raise ValueError(\n            f\"The denoising window [{denoising_start}, {denoising_end}] rounds to zero steps at \"\n            f\"steps={num_steps}. Increase steps or widen the window.\"\n        )\n    return window\n\n\ndef vae_normalize(latents: torch.Tensor, bn: torch.nn.Module, eps: float = 1e-5) -> torch.Tensor:\n    \"\"\"Apply the VAE's BatchNorm statistics to map encoder output -> transformer input.\n\n    The ERNIE-Image VAE wraps a BN layer that the upstream pipeline uses to normalize\n    latents before patchify (during img2img/inpaint encode) and to denormalize after\n    the denoise loop (before decode). This is the encode-side direction.\n    \"\"\"\n    mean = bn.running_mean.view(1, -1, 1, 1).to(latents.device, latents.dtype)\n    std = torch.sqrt(bn.running_var.view(1, -1, 1, 1) + eps).to(latents.device, latents.dtype)\n    return (latents - mean) / std\n\n","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/ernie_image/sampling_utils.py#L57-L93","documentation":"After slicing the sigma schedule to the requested window, if fewer than 2 sigmas remain the window yields zero denoising steps — the loop would return its input untouched and downstream would decode raw noise silently. The library raises ValueError to refuse this degenerate configuration.","triggerScenarios":"Calling get_schedule(num_steps, start, end) where int(num_steps*end) - int(num_steps*start) < 1, e.g. get_schedule(4, 0.9, 0.95) — a narrow denoise window with few steps.","commonSituations":"Very low step counts combined with partial-denoise sliders (small img2img strength); rounding at high denoising_start values (e.g. start=0.999 with 10 steps).","solutions":["Increase num_inference_steps so the window spans at least one interval.","Widen the [denoising_start, denoising_end] window.","In callers, validate steps*window_span >= 1 and adjust strength before invoking."],"exampleFix":"// before\nget_schedule(4, denoising_start=0.9, denoising_end=0.95)  # 0 steps\n// after\nget_schedule(40, denoising_start=0.9, denoising_end=0.95)  # 2 sigmas -> 1+ step","handlingStrategy":"validation","validationCode":"if int(num_steps * denoising_end) - int(num_steps * denoising_start) < 1:\n    num_steps = max(num_steps, math.ceil(1 / (denoising_end - denoising_start)))","typeGuard":"def yields_at_least_one_step(steps: int, start: float, end: float) -> bool:\n    return int(steps * end) - int(steps * start) >= 1","tryCatchPattern":"try:\n    sigmas = get_schedule(num_steps, denoising_start=s, denoising_end=e)\nexcept ValueError as e:\n    if \"zero steps\" in str(e):\n        num_steps = math.ceil(2 / (e - s if (e := denoising_end) > (s := denoising_start) else 0.1))\n        sigmas = get_schedule(num_steps, denoising_start=s, denoising_end=e)\n    else:\n        raise","preventionTips":["Scale step count with window width (steps >= ceil(1/span)).","Warn users when partial-denoise strength is tiny relative to step count.","Validate steps*window_span >= 1 in callers before scheduling."],"tags":["diffusion","scheduler","validation"],"backgroundTag":"invalid-parameter-range","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}