unslothai/unsloth · error · ValueError

guidance_start must be <= guidance_end

Error message

guidance_start must be <= guidance_end

What it means

Raised by a ControlNetSpec model_validator when guidance_start > guidance_end. Both are fractions in [0,1] of the denoise schedule; an inverted range would mean 'act over no steps', producing silently unguided output or a 500 deep in the denoise loop — rejected up front as a clean 422 instead.

Source

Thrown at studio/backend/models/inference.py:2924

    control_type: str = Field(
        "passthrough",
        description = "How to derive the control map: 'passthrough' (already a map) or 'canny'",
    )
    strength: float = Field(
        1.0, ge = 0.0, le = 2.0, description = "ControlNet conditioning scale; 0 disables"
    )
    guidance_start: float = Field(
        0.0, ge = 0.0, le = 1.0, description = "Fraction of steps at which ControlNet begins"
    )
    guidance_end: float = Field(
        1.0, ge = 0.0, le = 1.0, description = "Fraction of steps at which ControlNet ends"
    )

    @model_validator(mode = "after")
    def _check_guidance_range(self) -> "ControlNetSpec":
        # An inverted range means "act over no steps"; reject it as a clean 422 instead of a 500 deep in the denoise.
        if self.guidance_start > self.guidance_end:
            raise ValueError("guidance_start must be <= guidance_end")
        return self


class DiffusionGenerateRequest(BaseModel):
    """Request to generate one image from the loaded diffusion model."""

    prompt: str = Field(..., min_length = 1, description = "Text prompt")
    negative_prompt: Optional[str] = Field(
        None, description = "What to avoid (if the model supports it)"
    )
    width: int = Field(1024, ge = 256, le = 2048, description = "Image width in pixels (multiple of 16)")
    height: int = Field(
        1024, ge = 256, le = 2048, description = "Image height in pixels (multiple of 16)"
    )
    steps: int = Field(9, ge = 1, le = 100, description = "Number of denoising steps")
    guidance: float = Field(0.0, ge = 0.0, le = 20.0, description = "Classifier-free guidance scale")
    # le = 2**53-1: seeds round-trip through JSON recipes, where JavaScript rounds larger integers and a restored recipe would differ.
    seed: Optional[int] = Field(

View on GitHub (pinned to 203007d190)

Solutions

  1. Swap or clamp the values so guidance_start <= guidance_end.
  2. Enforce the ordering in the UI (when start moves past end, push end along).
  3. If you intended 'no guidance', omit the ControlNetSpec instead of an empty range.

Example fix

# before
controlnet = {"guidance_start": 0.8, "guidance_end": 0.2}

# after
start, end = sorted((0.8, 0.2))
controlnet = {"guidance_start": start, "guidance_end": end}
Defensive patterns

Strategy: validation

Validate before calling

def clamp_controlnet(spec: dict) -> dict:
    start, end = spec.get('guidance_start', 0.0), spec.get('guidance_end', 1.0)
    if start > end:
        start, end = end, start
    return {**spec, 'guidance_start': start, 'guidance_end': end}

Prevention

When it happens

Trigger: Sending controlnet: {"guidance_start": 0.8, "guidance_end": 0.2} with a diffusion generation request; commonly from swapping the two fields or from UI sliders that can cross.

Common situations: Two independent sliders for start/end where dragging one past the other is possible; configs authored with 'start when ControlNet ends' semantics reversed; copying examples from a tool that uses inverted notation.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/67cde11b129badd9. Report an issue: GitHub.