Skip to content

Commit e59b235

Browse files
committed
Validate num_inference_steps > 0 in DDPMScheduler.set_timesteps (#13394)
`DDPMScheduler.set_timesteps(num_inference_steps=0)` (or negative) silently left the scheduler with an empty schedule and stale state, instead of failing fast like other schedulers do. This made the bug invisible until a downstream `step()` call produced wrong results. Add the same `num_inference_steps must be a positive integer` check that `scheduling_block_refinement` already has, propagated to `DDPMScheduler` and its `# Copied from` sibling `DDPMSchedulerParallel` via `make fix-copies`.
1 parent 48f39c2 commit e59b235

3 files changed

Lines changed: 26 additions & 0 deletions

File tree

src/diffusers/schedulers/scheduling_ddpm.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ def set_timesteps(
308308
timesteps = np.array(timesteps, dtype=np.int64)
309309
self.custom_timesteps = True
310310
else:
311+
if num_inference_steps is None or num_inference_steps <= 0:
312+
# Without this guard, `num_inference_steps == 0` produces an empty
313+
# timestep schedule and downstream `step()` calls operate on stale
314+
# state — silent rather than failing fast (#13394).
315+
raise ValueError(f"`num_inference_steps` must be a positive integer, got {num_inference_steps}.")
311316
if num_inference_steps > self.config.num_train_timesteps:
312317
raise ValueError(
313318
f"`num_inference_steps`: {num_inference_steps} cannot be larger than `self.config.train_timesteps`:"

src/diffusers/schedulers/scheduling_ddpm_parallel.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ def set_timesteps(
323323
timesteps = np.array(timesteps, dtype=np.int64)
324324
self.custom_timesteps = True
325325
else:
326+
if num_inference_steps is None or num_inference_steps <= 0:
327+
# Without this guard, `num_inference_steps == 0` produces an empty
328+
# timestep schedule and downstream `step()` calls operate on stale
329+
# state — silent rather than failing fast (#13394).
330+
raise ValueError(f"`num_inference_steps` must be a positive integer, got {num_inference_steps}.")
326331
if num_inference_steps > self.config.num_train_timesteps:
327332
raise ValueError(
328333
f"`num_inference_steps`: {num_inference_steps} cannot be larger than `self.config.train_timesteps`:"

tests/schedulers/test_scheduler_ddpm.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,22 @@ def test_custom_timesteps_too_large(self):
190190
):
191191
scheduler.set_timesteps(timesteps=timesteps)
192192

193+
def test_set_timesteps_rejects_non_positive_num_inference_steps(self):
194+
# Regression for https://github.com/huggingface/diffusers/issues/13394:
195+
# `num_inference_steps=0` previously left the scheduler in an unusable
196+
# state silently. Both 0 and negative values should fail fast.
197+
scheduler_class = self.scheduler_classes[0]
198+
scheduler_config = self.get_scheduler_config()
199+
scheduler = scheduler_class(**scheduler_config)
200+
201+
with self.assertRaisesRegex(ValueError, r"`num_inference_steps` must be a positive integer"):
202+
scheduler.set_timesteps(num_inference_steps=0)
203+
with self.assertRaisesRegex(ValueError, r"`num_inference_steps` must be a positive integer"):
204+
scheduler.set_timesteps(num_inference_steps=-1)
205+
# Sanity check: a positive value still works after the rejected calls.
206+
scheduler.set_timesteps(num_inference_steps=10)
207+
self.assertEqual(scheduler.num_inference_steps, 10)
208+
193209
def test_full_loop_with_noise(self):
194210
scheduler_class = self.scheduler_classes[0]
195211
scheduler_config = self.get_scheduler_config()

0 commit comments

Comments
 (0)