gohugoio/hugo · error
gif: number of frame durations does not match number of fram
Error message
gif: number of frame durations does not match number of frames
What it means
When encoding an animated image (that is not already a raw *gif.GIF) to GIF, Hugo pulls the frames and per-frame durations from the AnimatedImage and requires the two slices to be the same length, since each GIF frame needs a delay entry. A mismatch means the source animation's metadata is inconsistent, so encoding aborts rather than guessing durations.
Source
Thrown at resources/images/codec.go:117
return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
case PNG:
encoder := png.Encoder{CompressionLevel: png.DefaultCompression}
return encoder.Encode(w, img)
case GIF:
if anim, ok := img.(himage.AnimatedImage); ok {
if g, ok := anim.GetRaw().(*gif.GIF); ok {
return gif.EncodeAll(w, g)
}
// Animated image, but not a GIF. Convert it.
frames := anim.GetFrames()
if len(frames) == 0 {
return gif.Encode(w, img, &gif.Options{NumColors: 256})
}
frameDurations := anim.GetFrameDurations()
if len(frameDurations) != len(frames) {
return errors.New("gif: number of frame durations does not match number of frames")
}
outGif := &gif.GIF{
Delay: himage.FrameDurationsToGifDelays(frameDurations),
}
outGif.LoopCount = anim.GetLoopCount()
for _, frame := range frames {
bounds := frame.Bounds()
palettedImage := image.NewPaletted(bounds, palette.Plan9)
draw.Draw(palettedImage, palettedImage.Rect, frame, bounds.Min, draw.Src)
outGif.Image = append(outGif.Image, palettedImage)
}
return gif.EncodeAll(w, outGif)
}
return gif.Encode(w, img, &gif.Options{View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Re-encode the source animation with a standard tool (e.g. `ffmpeg` or `img2webp`) so every frame carries a duration, then rebuild.
- Convert the asset to GIF outside Hugo and use the GIF directly as the source.
- If a well-formed file reproduces this, file a Hugo issue with the source image attached.
Defensive patterns
Strategy: validation
Validate before calling
// For animated gif encoding, ensure durations align with frames
if len(g.Delay) != len(g.Image) {
return fmt.Errorf("gif frame/delay mismatch: %d delays vs %d frames", len(g.Delay), len(g.Image))
} Try / catch
if err != nil && strings.Contains(err.Error(), "number of frame durations") {
// malformed animated gif source — re-export the asset or drop animation
} Prevention
- Validate animated GIF sources with a decoder pass before publishing
- Re-export problematic GIFs from the original tool rather than patching bytes
- When constructing gif.GIF programmatically, append Delay and Image together
When it happens
Trigger: Converting an animated source (e.g. animated WebP decoded via the WASM codec) to GIF output — `.Resize "300x gif"` or setting the target format to gif on an animated image whose decoded frame-duration list doesn't match its frame list.
Common situations: Malformed or unusually-authored animated WebP/AVIF files with truncated timing metadata; edge-case files produced by online GIF/WebP converters; codec bugs surfaced after the v0.153.0 image pipeline rework.
Related errors
- format %q not supported
- failed to decode gif: %w
- config needs a filename
- must provide an image and one or more filters
- cannot encode an empty string
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/ca3f2a7f8bc9cf21.json.
Report an issue: GitHub ↗.