gohugoio/hugo · error

metadata decoding failed

Error message

metadata decoding failed: %v

What it means

Returned (not panicked) by Decoder.DecodeMeta when the underlying multi-source metadata parser (EXIF + IPTC + XMP) panics. The deferred recover() at meta.go:305 catches the panic and converts it to an error with %v formatting. This is the newer multi-source metadata path, as opposed to the EXIF-only Decode method that produces error 554.

Solutions

  1. Strip problematic metadata with exiftool: 'exiftool -overwrite_original -all= path/to/image.jpg' then re-add what you need.
  2. Narrow imaging.meta.sources to only the sources you need — if XMP is causing failures, remove it: sources = ['exif', 'iptc'].
  3. Re-export the image with a reliable converter to produce clean metadata blocks.
  4. Use imaging.meta.fields patterns to exclude the specific fields that trigger the panic.

Example fix

# before (hugo.toml) — XMP parsing causes panic on some images
[imaging.meta]
sources = ["exif", "iptc", "xmp"]

# after — drop XMP (the slowest and most failure-prone source)
[imaging.meta]
sources = ["exif", "iptc"]
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate metadata before Hugo's DecodeMeta
// From the shell, check IPTC/XMP integrity:
// exiftool -verbose path/to/image.jpg 2>&1 | grep -i 'error|warning'

// Or restrict Hugo to EXIF+IPTC only (drop XMP, the most failure-prone):
// In hugo.toml:
// [imaging.meta]
// sources = ["exif", "iptc"]

Try / catch

// DecodeMeta recovers internally and returns an error.
// Handle gracefully:
metaInfo, err := decoder.DecodeMeta(filename, format, reader)
if err != nil {
    // err contains 'metadata decoding failed: <panic message>'
    log.Printf("warning: metadata decode failed for %s: %v", filename, err)
    metaInfo = &MetaInfo{} // proceed without metadata
}

Prevention

When it happens

Trigger: An image with malformed metadata in any of the configured sources (EXIF, IPTC, or XMP) causes the parser to panic during DecodeMeta. This path is used when imaging.meta.sources includes iptc or xmp in addition to exif.

Common situations: Enabling XMP source parsing (imaging.meta.sources = ['exif','iptc','xmp']) on images with corrupt XMP packets; images with IPTC-IIM data that violates the spec; HEIC-to-JPEG conversions that produce malformed XMP RDF.

Related errors


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

Appendix: source

Thrown at resources/images/meta/meta.go:305

	tags := make(map[string]any)
	for k, v := range tagInfos.All() {
		if !d.shouldIncludeField(k) {
			continue
		}
		tags[k] = v.Value
	}

	ex = &ExifInfo{Lat: lat, Long: long, Date: tm, Tags: tags}

	return
}

// DecodeMeta decodes metadata from all sources (EXIF, IPTC, XMP).
// Filename is only used for logging.
func (d *Decoder) DecodeMeta(filename string, format imagemeta.ImageFormat, r io.Reader) (m *MetaInfo, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("metadata decoding failed: %v", r)
		}
	}()

	var tagInfos imagemeta.Tags
	handleTag := func(ti imagemeta.TagInfo) error {
		tagInfos.Add(ti)
		return nil
	}

	shouldHandleTag := func(ti imagemeta.TagInfo) bool {
		// For EXIF, only include tags from IFD0 (skip thumbnail data).
		if ti.Source == imagemeta.EXIF {
			if !strings.HasPrefix(ti.Namespace, "IFD0") {
				return false
			}
		}

		return d.shouldIncludeField(ti.Tag)

View on GitHub (pinned to 52c9bd7908)