diff --git a/nodes_sampler.py b/nodes_sampler.py index 4dc208fe..a243d5f8 100644 --- a/nodes_sampler.py +++ b/nodes_sampler.py @@ -2753,6 +2753,28 @@ def INPUT_TYPES(s): CATEGORY = "WanVideoWrapper" EXPERIMENTAL = True + @classmethod + def VALIDATE_INPUTS(cls, steps, start_step, end_step, **kwargs): + # Run at prompt-queue time, before any node executes — this prevents + # an upstream sampler from loading the model and burning GPU time on + # work that a downstream sampler with an empty timestep slice will + # then crash on (UnboundLocalError: 'callback_latent' / 'noise_pred'). + if start_step >= steps: + return ( + f"start_step ({start_step}) must be < steps ({steps}); " + f"the requested range would produce an empty timestep slice " + f"and the sampler would crash mid-graph." + ) + if end_step != -1: + if end_step > steps: + return f"end_step ({end_step}) must be <= steps ({steps})." + if end_step <= start_step: + return ( + f"end_step ({end_step}) must be > start_step ({start_step}); " + f"the requested range would produce an empty timestep slice." + ) + return True + def process(self, scheduler, steps, start_step, end_step, shift, unique_id, sigmas=None, enhance_hf=False): sample_scheduler, timesteps, start_idx, end_idx = get_scheduler( scheduler, steps, start_step, end_step, shift, device, sigmas=sigmas, log_timesteps=True, enhance_hf=enhance_hf) diff --git a/wanvideo/schedulers/__init__.py b/wanvideo/schedulers/__init__.py index b5d6c621..727fd489 100644 --- a/wanvideo/schedulers/__init__.py +++ b/wanvideo/schedulers/__init__.py @@ -174,8 +174,22 @@ def get_scheduler(scheduler, steps, start_step, end_step, shift, device, transfo sample_scheduler.sigmas = torch.cat([timesteps / 1000, torch.zeros(1, device=timesteps.device)]) steps = len(timesteps) + # Reject ranges that produce an empty timestep slice — otherwise the + # sampler's main loop never executes and downstream code (e.g. callbacks + # referenced after the loop) crashes with a confusing UnboundLocalError. + if isinstance(start_step, int) and start_step >= steps: + raise ValueError( + f"start_step ({start_step}) must be < steps ({steps}); " + f"the requested range would produce an empty timestep slice" + ) + if isinstance(end_step, int) and end_step != -1 and end_step > steps: + raise ValueError( + f"end_step ({end_step}) must be <= steps ({steps})" + ) if (isinstance(start_step, int) and end_step != -1 and start_step >= end_step) or (not isinstance(start_step, int) and start_step != -1 and end_step >= start_step): - raise ValueError("start_step must be less than end_step") + raise ValueError( + f"start_step ({start_step}) must be less than end_step ({end_step})" + ) # Determine start and end indices for slicing start_idx = 0