gohugoio/hugo · error

exif init failed: %s

Error message

exif init failed: %s

What it means

Panic in imageResource.Exif() when the lazily-initialized exifInfoFn() returns an error. Hugo decodes EXIF metadata from the image (via Imaging.DecodeExif) and caches it; if decoding fails (corrupt EXIF, unsupported format, cache I/O error), the error is propagated as a panic from Exif(). The error string from the underlying failure is included.

Source

Thrown at resources/image.go:185

func (i *imageResource) newColorsFn() func() ([]images.Color, error) {
	return sync.OnceValues(func() ([]images.Color, error) {
		img, err := i.DecodeImage()
		if err != nil {
			return nil, err
		}
		colors := color_extractor.ExtractColors(img)
		result := make([]images.Color, len(colors))
		for j, c := range colors {
			result[j] = images.ColorGoToColor(c)
		}
		return result, nil
	})
}

func (i *imageResource) Exif() *meta.ExifInfo {
	x, err := i.root.exifInfoFn()
	if err != nil {
		panic(fmt.Sprintf("exif init failed: %s", err))
	}
	return x
}

func (i *imageResource) Meta() *meta.MetaInfo {
	m, err := i.root.metaInfoFn()
	if err != nil {
		panic(fmt.Sprintf("meta init failed: %s", err))
	}
	return m
}

func (i *imageResource) getImageMetaInfoCacheTargetPath() string {
	// Increment to invalidate the meta cache
	const imageMetaInfoVersionNumber = 1

	cfgHash := i.getSpec().Imaging.Cfg.SourceHash
	df := i.getResourcePaths()

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Validate the image file is a valid JPEG/TIFF with readable EXIF before calling .Exif.
  2. Check filesystem permissions on the Hugo cache directory (often resources/_gen).
  3. Re-export/repair the source image's EXIF, or strip EXIF if you do not need it.
  4. Guard template usage: {{ with .Exif }}...{{ end }} will still panic; instead pre-validate the resource or wrap usage in a partial that checks the image format.

Example fix

{{/* before: .Exif panics on a corrupt image */}}
{{ with $img.Exif }}{{ .Date }}{{ end }}

{{/* after: only call Exif for known photo formats; repair the source */}}
{{ if in (slice "jpg" "jpeg" "tiff") $img.MediaType.SubType }}
  {{ with $img.Exif }}{{ .Date }}{{ end }}
{{ end }}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the image is a photo format with readable EXIF before calling .Exif.
// Template side: gate on media type.
// Go side: decode a probe to ensure the file is a valid JPEG/TIFF.
func hasExif(subType string) bool {
    switch subType {
    case "jpeg", "jpg", "tiff":
        return true
    }
    return false
}

Try / catch

// Exif panics on decode failure; recover to degrade gracefully:
func safeExif(img *resources.ImageResource) (exif *meta.ExifInfo, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("exif decode failed: %v", r)
        }
    }()
    return img.Exif(), nil
}

Prevention

When it happens

Trigger: Calling .Exif on an image resource whose EXIF data is corrupt or unsupported by the decoder, or when the image file cache cannot be read/written. Passing a non-photographic image (SVG, some PNGs) where EXIF decoding fails. Permissions issues on the cache directory.

Common situations: A corrupt or truncated JPEG/TIFF uploaded to assets/images. A misconfigured imaging.exif config. Read-only cache filesystem. An image format the EXIF library cannot parse.

Related errors


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