{"record":{"id":"ca3f2a7f8bc9cf21","repo":"gohugoio/hugo","slug":"gif-number-of-frame-durations-does-not-match-numb","errorCode":null,"errorMessage":"gif: number of frame durations does not match number of frames","messagePattern":"gif: number of frame durations does not match number of frames","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resources/images/codec.go","lineNumber":117,"sourceCode":"\t\treturn jpeg.Encode(w, img, &jpeg.Options{Quality: quality})\n\tcase PNG:\n\t\tencoder := png.Encoder{CompressionLevel: png.DefaultCompression}\n\t\treturn encoder.Encode(w, img)\n\tcase GIF:\n\t\tif anim, ok := img.(himage.AnimatedImage); ok {\n\t\t\tif g, ok := anim.GetRaw().(*gif.GIF); ok {\n\t\t\t\treturn gif.EncodeAll(w, g)\n\t\t\t}\n\n\t\t\t// Animated image, but not a GIF. Convert it.\n\t\t\tframes := anim.GetFrames()\n\t\t\tif len(frames) == 0 {\n\t\t\t\treturn gif.Encode(w, img, &gif.Options{NumColors: 256})\n\t\t\t}\n\n\t\t\tframeDurations := anim.GetFrameDurations()\n\t\t\tif len(frameDurations) != len(frames) {\n\t\t\t\treturn errors.New(\"gif: number of frame durations does not match number of frames\")\n\t\t\t}\n\n\t\t\toutGif := &gif.GIF{\n\t\t\t\tDelay: himage.FrameDurationsToGifDelays(frameDurations),\n\t\t\t}\n\n\t\t\toutGif.LoopCount = anim.GetLoopCount()\n\n\t\t\tfor _, frame := range frames {\n\t\t\t\tbounds := frame.Bounds()\n\t\t\t\tpalettedImage := image.NewPaletted(bounds, palette.Plan9)\n\t\t\t\tdraw.Draw(palettedImage, palettedImage.Rect, frame, bounds.Min, draw.Src)\n\t\t\t\toutGif.Image = append(outGif.Image, palettedImage)\n\t\t\t}\n\n\t\t\treturn gif.EncodeAll(w, outGif)\n\t\t}\n\t\treturn gif.Encode(w, img, &gif.Options{","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/resources/images/codec.go#L99-L135","documentation":"When Hugo converts an animated (non-GIF) image to GIF, it pulls frames and their durations from the source animated image and rebuilds a `gif.GIF`. At codec.go:116 it asserts that the number of frame durations equals the number of frames; a mismatch would produce a corrupt animated GIF, so encoding is aborted.","triggerScenarios":"Calling an image transform that targets GIF output (e.g. `{{ $img.Resize \"100x100 gif\" }}` or `Fit`/`Crop` with `gif`) on an animated source image whose underlying AnimatedImage implementation returns inconsistent frame/duration counts.","commonSituations":"Animated WebP/AVIF source re-encoded to GIF via an image processing action; a malformed or truncated animated source; edge cases in the WASM codec frame extraction that yield zero durations for non-zero frames.","solutions":["Re-export or fix the source animated image so frames and durations align, then retry the transform.","Drop the `gif` target format from the action and keep the original format (or use PNG/JPEG for a single frame).","If the source is legitimately valid, file a Hugo bug with the source file — this indicates a bug in the AnimatedImage frame extraction, not in your config."],"exampleFix":"{{/* before */}}\n{{ $img.Resize \"200x200 gif\" }}\n\n{{/* after: keep lossless single-frame output */}}\n{{ $img.Resize \"200x200 png\" }}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-convert animated sources to GIF externally if you need animated GIF output.","Avoid targeting gif for animated non-GIF sources.","Report reproducible cases to Hugo — it indicates a frame-extraction bug."],"tags":["hugo","image-processing","gif","codec"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}