unslothai/unsloth · error · RuntimeError
GIF export decoded no frames.
Error message
GIF export decoded no frames.
What it means
Raised after the decode loop in _transcode_gif when the loop completed without appending a single frame ('if not frames'). Distinct from decode errors: the container opened, the stream exists, but zero frames were produced (e.g. the stream reports zero frames, or the step-skipping loop never yielded). There is nothing to feed frames[0].save(...), so it refuses before touching Pillow.
Source
Thrown at studio/backend/core/inference/video_gallery.py:253
if i % step:
continue
if len(frames) >= _GIF_MAX_FRAMES:
# Frame count unknown up front (no stream metadata): stop at the cap.
break
image = frame.to_image()
if max(image.size) > _GIF_MAX_EDGE:
scale = _GIF_MAX_EDGE / max(image.size)
image = image.resize(
(max(1, round(image.width * scale)), max(1, round(image.height * scale))),
Image.Resampling.LANCZOS,
)
frames.append(image.convert("P", palette = Image.Palette.ADAPTIVE))
except RuntimeError:
raise
except Exception as exc: # noqa: BLE001 -- surface as "decoder unavailable"
raise RuntimeError(f"GIF export failed to decode the clip: {exc}") from exc
if not frames:
raise RuntimeError("GIF export decoded no frames.")
duration_ms = max(20, int(1000 * step / rate))
buf = io.BytesIO()
frames[0].save(
buf,
format = "GIF",
save_all = True,
append_images = frames[1:],
duration = duration_ms,
loop = 0,
)
return buf.getvalue()
def _sidecar_path(video_id: str) -> Path:
return gallery_dir() / f"{video_id}.json"
# Sidecar keys every genuine Studio record carries. delete()/clear() own a pair only when its sidecar has all of these, so aView on GitHub (pinned to 203007d190)
Solutions
- Check the source stream's frame count before exporting (in_v.frames).
- Regenerate the clip; an empty video is an upstream failure, not an export settings problem.
- Reject zero-frame uploads at the API boundary.
Example fix
# before
_transcode_gif(Path("headers_only.mp4")) # RuntimeError('decoded no frames')
# after
import av
with av.open(str(p)) as c:
v = c.streams.video[0]
if not (v.frames or 0):
raise HTTPException(400, "clip contains no frames") Defensive patterns
Strategy: validation
Validate before calling
import av
def has_decoded_frames(path) -> bool:
with av.open(str(path)) as c:
if not c.streams.video:
return False
v = c.streams.video[0]
return bool(v.frames or 0) or next(iter(c.decode(video=0)), None) is not None Try / catch
try:
gif = _transcode_gif(path)
except RuntimeError as e:
if str(e) == "GIF export decoded no frames.":
regenerate_clip(path)
gif = _transcode_gif(path) # one retry on a fresh file only
else:
raise Prevention
- Check the stream's frame count before exporting.
- Treat zero-frame files as upstream write failures, never as export-config problems.
- Verify generation jobs wrote a nonzero frame count before marking clips done.
When it happens
Trigger: A container whose video stream exists but contains zero frames (headers only); a clip where in_v.frames is 0 and demuxing yields nothing; edge-case files with all frames consumed by a bogus step computation.
Common situations: Zero-byte-payload video files from interrupted writes; screen-recorder stubs that finalize an empty container.
Related errors
- GIF export failed: the clip has no video stream.
- Unknown model_kind '{model_kind}'. Expected one of {sorted(_
- A reference was sent empty.
- A reference decoded to no data.
- '{repo_id}' is a GGUF repo: pick one of its .gguf files (ggu
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/4748b1586fdd1519.
Report an issue: GitHub.