From 78a49eab33bdc7a88f028e2ace0c0ac7df63b6a5 Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Wed, 26 Aug 2026 02:00:12 +0100 Subject: [PATCH 1/2] fix(queue): stop the queue lock from suppressing exceptions Queue.__exit__ returned _queue_lock, and a truthy __exit__ return suppresses the exception in flight, so every `with queue_lock:` block discarded exceptions and resumed with locals from the aborted block unassigned. The bare threading.Lock it replaced returned None. --- modules/call_queue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/call_queue.py b/modules/call_queue.py index 466f44843..a77e8d0e0 100644 --- a/modules/call_queue.py +++ b/modules/call_queue.py @@ -25,7 +25,7 @@ class Queue: if _queue_debug: fn = f'{sys._getframe(3).f_code.co_name}:{sys._getframe(2).f_code.co_name}:{sys._getframe(1).f_code.co_name}' # pylint: disable=protected-access log.debug(f'Queue: unlock state={_queue_lock.locked()} fn={fn}') - return _queue_lock + # no return: a truthy __exit__ suppresses the exception in flight queue_lock = Queue() # public lock for external use From cb064474262011108f91bc1a5863d1a68074ec3f Mon Sep 17 00:00:00 2001 From: CalamitousFelicitousness Date: Wed, 26 Aug 2026 03:58:58 +0100 Subject: [PATCH 2/2] fix(video): report a cancelled generation as 499 process_images swallows the interrupt assertion, so a cancel reaches the shared video core as an empty result and was raised as 'processing failed' with 500, leaving clients unable to tell a cancel from a crash. Mirrors the LTX path, which already returns 499. --- modules/video_models/video_run.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/video_models/video_run.py b/modules/video_models/video_run.py index 97f76150c..db9ee06b8 100644 --- a/modules/video_models/video_run.py +++ b/modules/video_models/video_run.py @@ -307,6 +307,10 @@ def run(selected: models_def.Model, *, if err: raise VideoError(err, 500) if processed is None or (len(processed.images) == 0 and processed.bytes is None): + # process_images swallows the interrupt assertion, so an empty result is the only place + # a cancel and a genuine failure are still distinguishable + if shared.state.interrupted or shared.state.skipped: + raise VideoError('interrupted', 499) raise VideoError('processing failed', 500) log.info(f'Video: name="{selected.name}" cls={shared.sd_model.__class__.__name__} frames={len(processed.images)} time={t1-t0:.2f}')