gohugoio/hugo · error

failed to open image for decode

Error message

failed to open image for decode: %w

What it means

Returned by imageResource.DecodeImage when i.ReadSeekCloser() fails to open the underlying image source for reading. This wraps the open/read error with %w. ReadSeekCloser opens the resource's content (file, remote, or embedded) so failures here mean the source bytes are inaccessible before decoding even starts.

Solutions

  1. Confirm the image source file exists and is readable at build time.
  2. For remote/module resources, verify the URL resolves and network/cache is healthy.
  3. Unwrap the error to distinguish a missing file (os.ErrNotExist) from a permission error.

Example fix

// before: source deleted after resource registration
{{ $img := $res.Resize "100x" }}  // fails: file gone

// after: ensure asset exists / pin via module
{{ $img := resources.Get "images/logo.png" }}
{{ with $img }}{{ .Resize "100x" }}{{ end }}
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the source resource is readable before decoding.
func canOpen(res resource.Source) error {
    r, err := res.ReadSeekCloser()
    if err != nil { return err }
    r.Close()
    return nil
}

Try / catch

img, err := ir.DecodeImage()
if err != nil {
    if errors.Is(err, os.ErrNotExist) { /* source missing */ }
    return err
}

Prevention

When it happens

Trigger: Processing an image resource whose backing file was deleted/moved after registration, a remote image resource whose fetch fails, a resource on a filesystem (afero) that errors on Open, or permission denied on the source path.

Common situations: Source asset removed between build setup and image processing, a module/remote resource that 404s, a permissions issue on the working filesystem, or a symlink that dangles.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/72e4ee493221dcda. Report an issue: GitHub.

Appendix: source

Thrown at resources/image.go:475

		targetPath := i.relTargetPathFromConfig(conf, i.getSpec().Imaging.Cfg.SourceHash)
		ci.setTargetPath(targetPath)
		ci.Format = conf.TargetFormat
		ci.setMediaType(conf.TargetFormat.MediaType())

		return ci, converted, nil
	})
	if err != nil {
		return nil, err
	}
	return img, nil
}

// DecodeImage decodes the image source into an Image.
// This for internal use only.
func (i *imageResource) DecodeImage() (image.Image, error) {
	f, err := i.ReadSeekCloser()
	if err != nil {
		return nil, fmt.Errorf("failed to open image for decode: %w", err)
	}
	defer f.Close()

	return i.getSpec().Imaging.Codec.DecodeFormat(i.Format, f)
}

func (i *imageResource) clone(img image.Image) *imageResource {
	spec := i.baseResource.Clone().(baseResource)

	var image *images.Image
	if img != nil {
		image = i.WithImage(img)
	} else {
		image = i.WithSpec(spec)
	}

	ir := &imageResource{
		Image:        image,

View on GitHub (pinned to 52c9bd7908)