unslothai/unsloth · error · RuntimeError
WebM export failed: the clip has no video stream.
Error message
WebM export failed: the clip has no video stream.
What it means
Raised inside _transcode_webm right after opening the source clip when src.streams.video is empty, meaning the container opened but contains no video stream at all. It is a distinct, deliberate check before any encoder work so a bad input is not misreported as an encoder problem.
Source
Thrown at studio/backend/core/inference/video_gallery.py:143
dest = transcode_to_file(video_id, fmt)
if dest is None:
return None
try:
return dest.read_bytes()
finally:
dest.unlink(missing_ok = True)
def _transcode_webm(path: Path, dest: Path) -> None:
"""Transcode ``path`` to VP9 (+ Opus when the clip has audio) at ``dest``."""
try:
import av
except Exception as exc: # noqa: BLE001 -- no PyAV -> no transcode
raise RuntimeError("WebM export needs the 'av' package (PyAV).") from exc
try:
with av.open(str(path)) as src, av.open(str(dest), "w", format = "webm") as dst:
if not src.streams.video:
raise RuntimeError("WebM export failed: the clip has no video stream.")
in_v = src.streams.video[0]
rate = in_v.average_rate or 24
out_v = dst.add_stream("libvpx-vp9", rate = rate)
out_v.width = in_v.codec_context.width
out_v.height = in_v.codec_context.height
out_v.pix_fmt = "yuv420p"
# Realtime settings: VP9's default "good" profile is slow; cpu-used 8 + row-mt is much faster at a small quality cost.
out_v.options = {"deadline": "realtime", "cpu-used": "8", "row-mt": "1"}
# An LTX-2 clip carries a synchronized audio track and WebM is the web-embed format, so dropping it would hand back half the
# result. Opus is WebM's audio codec: resample to its 48 kHz grid and feed whole frames through a FIFO (960 samples per frame).
in_a = src.streams.audio[0] if src.streams.audio else None
out_a = fifo = resampler = None
if in_a is not None:
try:
stereo = (getattr(in_a.codec_context.layout, "nb_channels", 1) or 1) > 1
layout = "stereo" if stereo else "mono"
out_a = dst.add_stream("libopus", rate = 48000, layout = layout)
resampler = av.audio.resampler.AudioResampler(View on GitHub (pinned to 203007d190)
Solutions
- Inspect the source file: ffprobe input.mp4 (or av.open + src.streams) to confirm it has a video stream.
- Fix or re-generate the source clip so it actually contains video frames.
- If the file is valid elsewhere, check for codec support in the installed PyAV build.
Example fix
# before
_transcode_webm(Path("audio_only.mp4"), dest) # RuntimeError
# after: verify the source has a video track first
import av
with av.open(str(src_path)) as c:
assert c.streams.video, "source has no video stream" Defensive patterns
Strategy: validation
Validate before calling
import av
def has_video_stream(path) -> bool:
with av.open(str(path)) as c:
return bool(c.streams.video) Try / catch
try:
_transcode_webm(src, dest)
except RuntimeError as e:
if "no video stream" in str(e):
reject_source_clip() # input defect; do not retry, re-encode or regenerate
else:
raise Prevention
- Verify src.streams.video immediately after av.open on any file you produce.
- Treat 'opened but no video stream' as upstream corruption, not a settings issue.
- Log the file's ffprobe output when this fires.
When it happens
Trigger: Exporting to WebM a file that is audio-only or a still-image container; a truncated/corrupt clip whose video track is unparseable so PyAV reports no video streams; passing a file with only a cover-art stream.
Common situations: Pipeline bug upstream wrote an empty or audio-only file where a video was expected; user-uploaded file with the wrong extension (.mp4 that is really an audio file).
Related errors
- GIF export failed: the clip has no video stream.
- That reference video decoded to no frames.
- Unknown model_kind '{model_kind}'. Expected one of {sorted(_
- A reference was sent empty.
- A reference decoded to no data.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/0ac95f661c850f7b.
Report an issue: GitHub.