{"record":{"id":"7de7163affbc5b3a","repo":"unslothai/unsloth","slug":"gif-export-failed-to-decode-the-clip-exc","errorCode":null,"errorMessage":"GIF export failed to decode the clip: {exc}","messagePattern":"GIF export failed to decode the clip: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":501,"severity":"error","filePath":"studio/backend/core/inference/video_gallery.py","lineNumber":251,"sourceCode":"                step = -(-total // _GIF_MAX_FRAMES)\n            for i, frame in enumerate(src.decode(in_v)):\n                if i % step:\n                    continue\n                if len(frames) >= _GIF_MAX_FRAMES:\n                    # Frame count unknown up front (no stream metadata): stop at the cap.\n                    break\n                image = frame.to_image()\n                if max(image.size) > _GIF_MAX_EDGE:\n                    scale = _GIF_MAX_EDGE / max(image.size)\n                    image = image.resize(\n                        (max(1, round(image.width * scale)), max(1, round(image.height * scale))),\n                        Image.Resampling.LANCZOS,\n                    )\n                frames.append(image.convert(\"P\", palette = Image.Palette.ADAPTIVE))\n    except RuntimeError:\n        raise\n    except Exception as exc:  # noqa: BLE001 -- surface as \"decoder unavailable\"\n        raise RuntimeError(f\"GIF export failed to decode the clip: {exc}\") from exc\n    if not frames:\n        raise RuntimeError(\"GIF export decoded no frames.\")\n    duration_ms = max(20, int(1000 * step / rate))\n    buf = io.BytesIO()\n    frames[0].save(\n        buf,\n        format = \"GIF\",\n        save_all = True,\n        append_images = frames[1:],\n        duration = duration_ms,\n        loop = 0,\n    )\n    return buf.getvalue()\n\n\ndef _sidecar_path(video_id: str) -> Path:\n    return gallery_dir() / f\"{video_id}.json\"\n","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/video_gallery.py#L233-L269","documentation":"The catch-all of the GIF decode loop: any non-RuntimeError exception while demuxing/decoding frames or downscaling/resampling to paletted images is wrapped as 'GIF export failed to decode the clip' with the original exception attached. Deliberate RuntimeErrors from inside the try (no video stream) pass through unchanged.","triggerScenarios":"Corrupt or truncated clip raising av.FFVDecodeError / InvalidDataException mid-loop; a codec the installed PyAV build cannot decode; malformed frame dimensions breaking the PIL resize/resample step.","commonSituations":"Clips truncated by a crashed generation job or interrupted file transfer; exotic codecs in user uploads; PyAV builds missing decoders.","solutions":["Inspect the chained {exc} to identify the failing frame/codec.","Verify the clip decodes standalone: ffmpeg -v error -i clip.mp4 -f null - to surface corruption.","Reinstall av (pip install --force-reinstall av) if a missing decoder is at fault.","Regenerate the source clip if it is corrupt."],"exampleFix":"# before: exporting a truncated clip -> RuntimeError('failed to decode')\n\n# after: validate decodability first\nimport subprocess, sys\nrc = subprocess.run([\"ffmpeg\", \"-v\", \"error\", \"-i\", str(p), \"-f\", \"null\", \"-\"]).returncode\nif rc != 0:\n    raise HTTPException(400, \"clip is not decodable; regenerate it\")","handlingStrategy":"try-catch","validationCode":"import subprocess\n\ndef decodable(path) -> bool:\n    return subprocess.run(\n        [\"ffmpeg\", \"-v\", \"error\", \"-i\", str(path), \"-f\", \"null\", \"-\"],\n        capture_output=True,\n    ).returncode == 0","typeGuard":null,"tryCatchPattern":"try:\n    gif = _transcode_gif(path)\nexcept RuntimeError as e:\n    if \"failed to decode\" in str(e):\n        log.warning(\"clip corrupt: %s\", e.__cause__)\n        raise HTTPException(400, \"clip is corrupt; regenerate it\") from e\n    raise","preventionTips":["Decode-validate generated clips as part of the generation job itself.","Inspect __cause__ — it carries the real decode error.","Keep ffmpeg installed in ops environments for quick ffprobe triage."],"tags":["video","gif","decode","corrupt-file"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}