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

  1. Open the file locally or run `file <path>` to confirm it's a real GIF; replace or re-download if corrupt.
  2. 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.
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/eaa62c8f82bb68e4.json. Report an issue: GitHub ↗.