zed-industries/zed · error

GIF could not be decoded: all frames failed

Error message

GIF could not be decoded: all frames failed

What it means

When decoding an animated image, each GIF frame is decoded individually and frames that error are skipped with a debug log. If every frame fails, the collected frame list is empty and the decoder bails — the file exists and is classified as GIF, but no frame could be produced (corrupt, truncated, or using constructs the image decoder rejects).

Source

Thrown at crates/gpui/src/platform.rs:2773

        Self::iter()
            .find(|format| format.mime_type() == mime_type)
            .or_else(|| Self::from_mime_type_alias(mime_type))
    }

    /// Non-canonical mime types that some producers use in the wild.
    /// Unlike `mime_type()` which returns the single canonical form,
    /// these are legacy or shortened variants we still need to recognize.
    fn from_mime_type_alias(mime_type: &str) -> Option<Self> {
        match mime_type {
            "image/jpg" => Some(Self::Jpeg),
            "image/tif" => Some(Self::Tiff),
            _ => None,
        }
    }
}

/// An image, with a format and certain bytes
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Image {
    /// The image format the bytes represent (e.g. PNG)
    pub format: ImageFormat,
    /// The raw image bytes
    pub bytes: Vec<u8>,
    /// The unique ID for the image
    pub id: u64,
}

pub(crate) fn decode_static_image(
    bytes: &[u8],
    format: image::ImageFormat,
) -> Result<SmallVec<[Frame; 1]>> {
    let decoder = image::ImageReader::with_format(Cursor::new(bytes), format)
        .into_decoder()
        .context("creating image decoder")?;
    decode_static_image_from_decoder(decoder)
}

View on GitHub (pinned to 9d272b0363)

Solutions

  1. Verify the file is a complete, real GIF (open in a browser, or run 'file icon.gif')
  2. Re-download or re-encode the asset (ffmpeg or gifsicle) if it is corrupt
  3. Treat decode failure as non-fatal: log, show a placeholder or first-frame fallback, and continue rendering

Example fix

// before: caller assumes decode always succeeds
let frames = image.frames()?;

// after: degrade gracefully on all-frames-failed
let frames = match image.frames() {
    Ok(frames) => frames,
    Err(err) => {
        log::warn!("animated image decode failed: {err}");
        return render_placeholder(image.id());
    }
};
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap pre-check before decoding a downloaded asset
fn looks_like_complete_gif(bytes: &[u8]) -> bool {
    bytes.starts_with(b"GIF8") && bytes.ends_with(&[0x3b]) // GIF trailer byte
}

Try / catch

match image.frames() {
    Ok(frames) => render(frames),
    Err(err) => {
        log::warn!("animated decode failed: {err}");
        render_placeholder() // never propagate into the render loop
    }
}

Prevention

When it happens

Trigger: Loading a truncated GIF (interrupted download), a corrupt file, or a GIF whose frames all hit decode errors, leaving the frames vector empty after the loop.

Common situations: Broken avatars or emoji fetched over flaky networks; partially-written cache files; an HTML error page saved with a .gif extension; GIFs re-encoded by lossy pipelines.

Related errors


AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20). Data as JSON: /api/errors/6a64c270942cbaca. Report an issue: GitHub.