gohugoio/hugo · error
failed to decode gif: %w
Error message
failed to decode gif: %w
What it means
When decoding a resource identified as GIF, Hugo uses gif.DecodeAll to read all frames (to preserve animation), wrapping any decode failure in this error. It means the file's bytes are not a valid GIF stream even though the pipeline classified it as GIF, or the stream is truncated/corrupt mid-frame.
Source
Thrown at resources/images/codec.go:189
logg.StringFunc(
func() string {
return fmt.Sprintf("Decoding image from format %s", f)
},
),
)
switch f {
case JPEG, PNG:
// We reworked this decode/encode setup to get full WebP support in v0.153.0.
// In the first take of that we used f to decide whether to call png.Decode or jpeg.Decode here,
// but testing it on some sites, it seems that it's not uncommon to store JPEGs with PNG extensions and vice versa.
// So, to reduce some noise in that release, we fallback to the standard library here,
// which will read the magic bytes and decode accordingly.
img, _, err := image.Decode(r)
return img, err
case GIF:
g, err := gif.DecodeAll(r)
if err != nil {
return nil, fmt.Errorf("failed to decode gif: %w", err)
}
if len(g.Delay) > 1 {
return &giphy{gif: g, Image: g.Image[0]}, nil
}
return g.Image[0], nil
case TIFF:
return tiff.Decode(r)
case BMP:
return bmp.Decode(r)
case AVIF:
return d.avif.Decode(r)
case WEBP:
img, err := d.webp.Decode(r)
if err == nil {
return img, nil
}
if rs, ok := r.(io.ReadSeeker); ok {
// See issue 14288. Turns out it's not uncommon to e.g. name their PNG files with a WEBP extension.View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Open the file locally or run `file <path>` to confirm it's a real GIF; replace or re-download if corrupt.
- If it's actually another format (often webp or mp4), rename it with the correct extension or convert it to a true GIF with `ffmpeg`/ImageMagick.
- Run `git lfs pull` if LFS-tracked binaries are missing in the build environment.
Example fix
// before: 'animation.gif' is actually a WebP $ file assets/animation.gif # RIFF ... Web/P // after $ mv assets/animation.gif assets/animation.webp # and update template refs
Defensive patterns
Strategy: try-catch
Validate before calling
f, err := os.Open(path)
if err != nil { return err }
defer f.Close()
if _, err := gif.DecodeAll(f); err != nil {
return fmt.Errorf("corrupt gif %q: %w", path, err)
} Try / catch
if err != nil && strings.Contains(err.Error(), "failed to decode gif") {
// corrupt or non-gif file with .gif extension — quarantine the asset and report it
} Prevention
- Verify GIF magic bytes (GIF87a/GIF89a) for files with .gif extensions
- Validate third-party/user-supplied GIFs at ingestion, not at build time
- Keep original source assets so corrupt files can be re-exported
When it happens
Trigger: Processing a .gif resource (`.Resize`, `.Process`, width/height access on the decoded image path) whose contents fail gif.DecodeAll — corrupt frames, truncation, or a non-GIF file with a .gif extension.
Common situations: Partially downloaded or corrupted GIFs in assets/; other formats (webp, mp4 'gifs' from social platforms) saved with a .gif extension; Git LFS pointer stubs in place of the binary.
Related errors
- failed to load image config: %w
- gif: number of frame durations does not match number of fram
- failed to decode config for language %q: %w
- failed to decode %q: %w
- local ref %q not found
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/eaa62c8f82bb68e4.json.
Report an issue: GitHub ↗.