sgl-project/sglang · error · HTTPException

Video file is corrupted or cannot be decoded

Error message

Video file is corrupted or cannot be decoded

What it means

An HTTPException (status 432, a custom code) raised by _preprocess_video_sync when _decode_frames_and_timestamps raises any exception during video decoding. It converts arbitrary decode failures (corrupt container, unsupported codec, truncated file) into a client-facing 'video is corrupted' response.

Source

Thrown at python/sglang/srt/multimodal/processors/mimo_v2.py:1670

        ).build(_processor)

    @property
    def spatial_merge_size(self):
        return self.vision_config.spatial_merge_size

    def _preprocess_video_sync(self, vdw, preprocess_kwargs=None):
        # Seed with processor_config defaults so E/D agree on fps/min/max.
        default_kwargs = {
            k: v
            for k, v in self.mimo_processor.default_video_processor_kwargs.items()
            if v is not None and k in ("fps", "min_frames", "max_frames", "num_frames")
        }
        ele = {**default_kwargs, **(preprocess_kwargs or {})}
        try:
            return _decode_frames_and_timestamps(vdw, ele)
        except Exception as e:
            logger.error(f"Video decode failed in _preprocess_video_sync: {e}")
            raise HTTPException(
                status_code=432, detail="Video file is corrupted or cannot be decoded"
            )

    def process_mm_data(
        self, input_text, images=None, videos=None, audios=None, **kwargs
    ) -> dict:
        if audios and not self.AUDIO_TOKEN_REGEX.search(input_text or ""):
            input_text = f"{self.mm_tokens.audio_token}{input_text or ''}"

        processed_images = []
        processed_videos = []
        processed_audios = []

        if images:
            processed_images = list(images)

        if videos:
            for video in videos:

View on GitHub (pinned to 0132848349)

Solutions

  1. Verify the video plays and decodes locally (ffprobe file) before sending
  2. Re-fetch/re-upload the source file if the downloaded bytes are truncated or an error page
  3. Re-encode to a widely supported format (H.264 MP4) if codec support is the issue
  4. If using pre-signed URLs, ensure they are fresh and accessible from the server

Example fix

# before: url returns HTML error page → HTTP 432
video = {'url': expired_presigned_url}
# after
video = {'url': fresh_presigned_url}  # curl -I shows 200 + video/mp4
Defensive patterns

Strategy: try-catch

Validate before calling

import subprocess
def video_ok(path_or_url):
    try:
        subprocess.run(['ffprobe', '-v','error', path_or_url], check=True, timeout=30,
                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return True
    except Exception:
        return False

assert video_ok(url), 'video not decodable; refusing to send'

Try / catch

try:
    out = await client.generate(prompt, video=url)
except HTTPException as e:  # sglang client maps 432
    if e.status_code == 432:
        re_upload_and_retry(url)  # re-fetch source, verify with ffprobe, retry once
    else:
        raise

Prevention

When it happens

Trigger: Sending a request whose video URL/bytes cannot be decoded by VideoDecoderWrapper — truncated download, non-video file, unsupported codec/container, or an unreadable URL — on the async path process_mm_data_async.

Common situations: Expired/invalid pre-signed URLs returning an HTML error page instead of video; partially uploaded files; exotic codecs (e.g. AV1/HEVC builds without support); network hiccups truncating the body.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/7934858b4004bb71. Report an issue: GitHub.