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
- Verify the file is a complete, real GIF (open in a browser, or run 'file icon.gif')
- Re-download or re-encode the asset (ffmpeg or gifsicle) if it is corrupt
- 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
- Validate downloaded images (magic bytes, content-type) before caching them
- Keep decode failures non-fatal in UI: placeholder plus log
- Re-encode user-supplied GIFs through a sanitizer on ingest
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
- Device lost: {err}
- database not initialized
- blocking sender returned without value
- Failed to get active display list. Result: {result}
- in #[action] attribute: {}
AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20).
Data as JSON: /api/errors/6a64c270942cbaca.
Report an issue: GitHub.