gohugoio/hugo · error
webp: animated image has no frames
Error message
webp: animated image has no frames
What it means
Returned by WebpCodec.Encode in internal/warpc/webp.go:273 for the concrete *AnimatedImage type when GetFrames() returns an empty slice. The encoder needs at least one frame to derive bounds/stride before dispatching the encode command to WASM.
Solutions
- Validate len(img.GetFrames()) > 0 before calling Encode.
- Re-decode the source animation from the original file; a truncated download can yield zero frames.
- Fall back to single-frame encoding when the animation is actually a still.
Example fix
// before
err := webpCodec.Encode(w, animImg, nil)
// after
if len(animImg.GetFrames()) == 0 {
return errors.New("cannot encode: animation has no frames")
}
err := webpCodec.Encode(w, animImg, nil) Defensive patterns
Strategy: type-guard
Validate before calling
// Check frames before encoding an *AnimatedImage.
if a, ok := img.(*AnimatedImage); ok && len(a.GetFrames()) == 0 {
return errors.New("skip: animation has no frames")
} Type guard
func hasFrames(a *AnimatedImage) bool { return len(a.GetFrames()) > 0 } Prevention
- Validate frame count right after decoding the animation.
- Re-decode from the original file if frames are missing.
- Fall back to single-frame encode for stills.
When it happens
Trigger: Passing an *AnimatedImage (warpc's own animated type) with zero frames into WebpCodec.Encode. Typically a decode step produced an animated image object whose frame list was never populated or was cleared.
Common situations: A GIF/animated source that decoded to zero frames (corrupt or truncated animation), or a programmatic AnimatedImage construction that skipped frame insertion.
Related errors
- {}
- webp: image is too large to encode
- [ ] Error reading blob data (size: %llu read: %zu) \n
- Error creating WebPAnimEncoder\n
- Error: Line did not parse to a valid JSON object\n
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/a83a236f57b62ea3.
Report an issue: GitHub.
Appendix: source
Thrown at internal/warpc/webp.go:273
var (
bounds = img.Bounds()
imageBytes []byte
stride int
frameDurations []int
loopCount int
command string
)
switch v := img.(type) {
case *image.RGBA:
imageBytes = v.Pix
stride = v.Stride
command = commandEncodeNRGBA
case *AnimatedImage:
// Animated WebP.
frames := v.GetFrames()
if len(frames) == 0 {
return errors.New("webp: animated image has no frames")
}
firstFrame := frames[0]
bounds = firstFrame.Bounds()
nrgba := convertToNRGBA(firstFrame)
frameSize := nrgba.Stride * bounds.Dy()
imageBytes = make([]byte, frameSize*len(frames))
stride = nrgba.Stride
for i, frame := range frames {
var nrgbaFrame *image.NRGBA
if i == 0 {
nrgbaFrame = nrgba
} else {
nrgbaFrame = convertToNRGBA(frame)
}
copy(imageBytes[i*frameSize:(i+1)*frameSize], nrgbaFrame.Pix)
}
frameDurations = v.GetFrameDurations()
loopCount = v.loopCountView on GitHub (pinned to 52c9bd7908)