invoke-ai/InvokeAI · error · RuntimeError

Unable to open video decoder output stream

Error message

Unable to open video decoder output stream

What it means

Raised when the spawned ffmpeg/worker process used for streamed frame iteration was created but its stdout or stderr pipe is None, meaning the output streams could not be opened. This is essentially an OS-level pipe-creation/resource failure or an invalid Popen configuration. The worker process tree is terminated before raising to avoid leaking processes.

Source

Thrown at invokeai/app/util/video_thumbnails.py:244

    is_canceled: Optional[Callable[[], bool]] = None,
    first_frame_timeout: Optional[float] = None,
) -> Iterator[np.ndarray]:
    """Streams decoded frames from an isolated worker with bounded memory and wait time.

    ``timeout`` bounds decoder *inactivity*: it is restarted after every frame, so a long
    video is not killed for being long. ``first_frame_timeout`` overrides that budget for
    the first frame only, letting a caller that already spent part of the budget waiting
    for capacity charge that wait against the same deadline instead of granting a fresh one.
    """
    proc = _spawn_worker(
        "stream",
        str(video_path),
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    if proc.stdout is None or proc.stderr is None:
        _terminate_process_tree(proc)
        raise RuntimeError("Unable to open video decoder output stream")
    memory_monitor_stop, _memory_exceeded, memory_monitor = _start_worker_memory_monitor(proc)

    results: queue.Queue[tuple[str, object]] = queue.Queue(maxsize=1)
    stopped = threading.Event()
    stderr_tail = bytearray()

    def read_stderr() -> str:
        return bytes(stderr_tail).decode(errors="replace").strip()

    def drain_stderr() -> None:
        while chunk := proc.stderr.read(4096):
            stderr_tail.extend(chunk)
            if len(stderr_tail) > MAX_DECODE_STDERR_BYTES:
                del stderr_tail[:-MAX_DECODE_STDERR_BYTES]

    def read_exactly(size: int) -> bytes:
        chunks: list[bytes] = []
        remaining = size

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Raise the file-descriptor limit (ulimit -n / container RLIMIT_NOFILE, e.g. 65536) and retry.
  2. Check for fd leaks in the application (lsof | wc -l) — leaked pipes from previous jobs will exhaust the limit.
  3. Reduce concurrency of iter_video_frames calls so fewer simultaneous pipes are open.
  4. If the error persists on a stock install, report it — with the library's own Popen config this should be unreachable.

Example fix

// before
for frame in iter_video_frames(path): ...  # RuntimeError: Unable to open video decoder output stream
// after
import resource
resource.setrlimit(resource.RLIMIT_NOFILE, (65536, 65536))  # in service startup
for frame in iter_video_frames(path): ...
Defensive patterns

Strategy: retry

Validate before calling

# check fd headroom before heavy video work
import os, resource
soft, _ = resource.getrlimit(resource.RLIMIT_NOFILE)
assert soft > 1024, f"fd limit too low: {soft}"

Try / catch

try:
    for frame in iter_video_frames(path):
        ...
except RuntimeError as e:
    if "output stream" in str(e):
        raise FdExhaustionError("raise RLIMIT_NOFILE and retry") from e

Prevention

When it happens

Trigger: subprocess.Popen returning a process whose stdout/stderr is None despite stdout=subprocess.PIPE — practically only when pipes fail to allocate (fd exhaustion, ulimit -n too low), or if the Popen call was modified to not use PIPE.

Common situations: Servers with very low file-descriptor limits running many concurrent video jobs; containers with small RLIMIT_NOFILE; a fd leak elsewhere in the app exhausting descriptors.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/43a9dab46119281d. Report an issue: GitHub.