gohugoio/hugo · error
failed to decode gif
Error message
failed to decode gif: %w
What it means
Returned by Codec.DecodeFormat in the GIF branch when gif.DecodeAll fails on the reader. It wraps the standard library's decode error with %w. GIFs are decoded via gif.DecodeAll (not image.Decode) to support animated multi-frame GIFs, so any malformation the GIF decoder rejects surfaces here.
Solutions
- Validate the file is a real GIF (check magic bytes 'GIF87a'/'GIF89a') before processing.
- Re-download or replace the corrupted GIF asset.
- Inspect the wrapped error from image/gif for specifics (e.g. 'gif: invalid header').
Example fix
// before: assets/banner.gif is actually a renamed png
{{ $img := resources.Get "images/banner.gif" }}
{{ $resized := $img.Resize "300x" }} // fails: gif decode
// after: fix the asset extension to match content
{{ $img := resources.Get "images/banner.png" }}
{{ $resized := $img.Resize "300x" }} Defensive patterns
Strategy: validation
Validate before calling
// Sniff GIF magic bytes before processing.
func isProbablyGIF(b []byte) bool {
return len(b) >= 6 && (string(b[:6]) == "GIF87a" || string(b[:6]) == "GIF89a")
} Type guard
func looksLikeGIF(header []byte) bool {
return len(header) >= 6 && (string(header[:6]) == "GIF87a" || string(header[:6]) == "GIF89a")
} Try / catch
if _, err := codec.DecodeFormat(images.GIF, r); err != nil {
if strings.Contains(err.Error(), "failed to decode gif") { /* replace corrupt asset */ }
} Prevention
- Ensure .gif files are genuinely GIF-encoded, not renamed.
- Validate downloads completed (not truncated) before processing.
- Keep a CI check that decodes all committed GIFs.
When it happens
Trigger: Processing a .gif resource whose bytes are truncated, corrupted, or not actually a valid GIF (e.g. a file renamed to .gif). gif.DecodeAll returns an error and this wraps it.
Common situations: A .gif file that is actually a JPEG/PNG renamed; a truncated download; a GIF with an invalid header or block; an empty file served as a GIF.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- gif: number of frame durations does not match number of…
- exif failed
- failed to decode build config in front matter
- failed to decode image
- failed to decode image
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/eaa62c8f82bb68e4.
Report an issue: GitHub.
Appendix: 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 52c9bd7908)