invoke-ai/InvokeAI · error · TimeoutError
Timed out waiting to decode frames from {video_path}
Error message
Timed out waiting to decode frames from {video_path} What it means
Raised by iter_video_frames when it cannot acquire the streaming capacity slots (_VIDEO_STREAM_SLOTS then _VIDEO_DECODER_SLOTS) within the capacity deadline, which is shared with the first-frame timeout. This bounds total concurrent streamers+decoders; the shared deadline prevents waiting ~2x timeout for slot plus first frame. Cancellation is checked while waiting.
Source
Thrown at invokeai/app/util/video_thumbnails.py:355
stderr_reader.join(timeout=1)
proc.stderr.close()
def iter_video_frames(
video_path: Path,
timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS,
is_canceled: Optional[Callable[[], bool]] = None,
) -> Iterator[np.ndarray]:
acquired: list[threading.BoundedSemaphore] = []
capacity_deadline = time.monotonic() + timeout
try:
for slot in (_VIDEO_STREAM_SLOTS, _VIDEO_DECODER_SLOTS):
while True:
if is_canceled is not None and is_canceled():
raise CanceledException
remaining = capacity_deadline - time.monotonic()
if remaining <= 0:
raise TimeoutError(f"Timed out waiting to decode frames from {video_path}")
if slot.acquire(timeout=min(0.1, remaining)):
acquired.append(slot)
break
# Charge the capacity wait against the same deadline as the first frame, the way
# _run_worker does. Handing the decoder a fresh full timeout here would let a
# caller that waited just under `timeout` for a slot block for nearly 2 * timeout
# before failing — twice the bound the callers (upload probing, node decodes)
# believe they are enforcing. Later frames still get a full `timeout` each: after
# the first frame the budget is an inactivity bound, not a queueing one.
yield from _iter_video_frames_unbounded(
video_path,
timeout,
is_canceled,
first_frame_timeout=max(0.0, capacity_deadline - time.monotonic()),
)
finally:
for slot in reversed(acquired):
slot.release()View on GitHub (pinned to 0b6a024f2f)
Solutions
- Ensure generators are fully consumed or explicitly closed (gen.close() / context management) so slots are released promptly.
- Raise the timeout to tolerate queueing, or serialize video jobs through an application-level queue.
- Reduce concurrent callers; the slot limits are intentional back-pressure, so scale workers/machines instead.
- If slots appear permanently stuck, look for leaked generator objects or hung worker processes holding the semaphores.
Example fix
// before
gen = iter_video_frames(path)
first = next(gen) # TimeoutError waiting for slots under load
# gen abandoned → slot leaked
// after
with contextlib.closing(iter_video_frames(path, timeout=120)) as gen:
for frame in gen:
process(frame) Defensive patterns
Strategy: try-catch
Try / catch
try:
gen = iter_video_frames(path, timeout=120)
for frame in gen:
process(frame)
except TimeoutError as e:
if "waiting to decode frames" in str(e):
backoff_and_requeue(path) # pool saturated, retry later
finally:
gen.close() Prevention
- Always close generators (try/finally or contextlib.closing)
- Queue video work instead of unbounded parallel streaming
- Keep timeout >= expected slot-wait + first-frame decode
- Scale decoder capacity with upload throughput
When it happens
Trigger: Calling iter_video_frames while the maximum number of concurrent stream and decoder slots are held for longer than `timeout`; heavy concurrent thumbnail/frame extraction load; slots leaked by a stuck earlier job; timeout set too small relative to queue depth.
Common situations: Burst uploads to a small server saturating decoder slots; long-running stream consumers holding stream slots; load tests opening many iter_video_frames generators at once without closing them (unclosed generators never release slots).
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Timeout exceeded
- Video decode worker timed out after {timeout}s
- Timed out decoding frames from {video_path}
- {message}: {detail}
- Video must use a browser-compatible H.264/AVC codec
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/b0fab570c2fc96c0.
Report an issue: GitHub.