huggingface/candle · error

timestep out of this schedulers bounds: {timestep}

Error message

timestep out of this schedulers bounds: {timestep}

What it means

EulerAncestralDiscreteScheduler::scale_model_input looks up the requested timestep in its precomputed timesteps list to find the step_index used to index sigmas. If the timestep is not present in that list, the scheduler cannot compute sigma and bails. The scheduler only accepts exact timesteps it generated at construction time.

Source

Thrown at candle-transformers/src/models/stable_diffusion/euler_ancestral_discrete.rs:162

            init_noise_sigma,
            config,
        })
    }
}

impl Scheduler for EulerAncestralDiscreteScheduler {
    fn timesteps(&self) -> &[usize] {
        self.timesteps.as_slice()
    }

    /// Ensures interchangeability with schedulers that need to scale the denoising model input
    /// depending on the current timestep.
    ///
    /// Scales the denoising model input by `(sigma**2 + 1) ** 0.5` to match the K-LMS algorithm
    fn scale_model_input(&self, sample: Tensor, timestep: usize) -> Result<Tensor> {
        let step_index = match self.timesteps.iter().position(|&t| t == timestep) {
            Some(i) => i,
            None => bail!("timestep out of this schedulers bounds: {timestep}"),
        };

        let sigma = self
            .sigmas
            .get(step_index)
            .expect("step_index out of sigma bounds - this shouldn't happen");

        sample / ((sigma.powi(2) + 1.).sqrt())
    }

    /// Performs a backward step during inference.
    fn step(&mut self, model_output: &Tensor, timestep: usize, sample: &Tensor) -> Result<Tensor> {
        let step_index = self
            .timesteps
            .iter()
            .position(|&p| p == timestep)
            .ok_or_else(|| Error::Msg("timestep out of this schedulers bounds".to_string()))?;

View on GitHub (pinned to d5fee525bf)

Solutions

  1. Only pass timesteps taken directly from scheduler.timesteps() (or the value yielded by the scheduler's iteration API)
  2. Re-run set_timesteps(num_inference_steps) and use the resulting list verbatim
  3. Check for integer truncation when converting float timesteps; match the scheduler's rounding

Example fix

// before
for t in 0..num_steps { scheduler.scale_model_input(x, t)?; }
// after
for &t in scheduler.timesteps().iter() { scheduler.scale_model_input(x, t)?; }
Defensive patterns

Strategy: validation

Validate before calling

if !scheduler.timesteps().iter().any(|&t| t == timestep) {
    return Err(anyhow::anyhow!("timestep {} not in scheduler timesteps", timestep));
}

Try / catch

let out = match scheduler.scale_model_input(sample, t) {
    Ok(x) => x,
    Err(e) if e.to_string().contains("out of this schedulers bounds") => {
        anyhow::bail!("use timesteps from scheduler.timesteps(), got {}", t)
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling scale_model_input(sample, timestep) with a timestep value that is not exactly one of the entries in scheduler.timesteps (from set_timesteps/init), including off-by-one, float-rounded, or out-of-range values.

Common situations: Reimplementing a diffusion loop and computing timesteps independently instead of using scheduler.timesteps(); iterating a float sigma schedule and casting to usize differently than the scheduler; reusing a scheduler after re-initializing timesteps with a different step count.

Related errors


AI-assisted analysis of huggingface/candle@d5fee525bf (2026-09-02). Data as JSON: /api/errors/151870eaf45cc149. Report an issue: GitHub.