gohugoio/hugo · error

invalid metadata source

Error message

invalid metadata source %q in imaging.meta.sources config; must be one of %s

What it means

Validates each entry in imaging.meta.sources after lowercasing. The only allowed values are 'exif', 'iptc', and 'xmp' (defined in the validMetaSources map). The error message lists all valid keys, sorted alphabetically. This controls which metadata standards Hugo extracts from images.

Solutions

  1. Check the error message — it prints the invalid source and the sorted list of valid sources.
  2. Fix imaging.meta.sources in hugo.toml to contain only values from {exif, iptc, xmp}.
  3. Remove the sources key to use the default of ['exif', 'iptc'] (XMP is excluded by default for performance).

Example fix

# before (hugo.toml)
[imaging.meta]
sources = ["exif", "iptc", "tiff"]

# after
[imaging.meta]
sources = ["exif", "iptc", "xmp"]
Defensive patterns

Strategy: validation

Validate before calling

// Validate metadata sources before decoding
func validateMetaSources(sources []string) error {
    valid := map[string]bool{"exif": true, "iptc": true, "xmp": true}
    for _, s := range sources {
        if !valid[strings.ToLower(s)] {
            return fmt.Errorf("invalid metadata source %q; valid: exif, iptc, xmp", s)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: Setting imaging.meta.sources in hugo.toml to a list containing any string other than exif, iptc, or xmp (case-insensitive, since values are normalized to lowercase before checking).

Common situations: Typing 'EXIF' in uppercase (harmless — normalized), 'tiff' or 'raw' (not supported), or 'rdf' (confusing XMP's underlying format with the source name). Also from copying an example that references a source removed or renamed in a newer Hugo version.

Related errors


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

Appendix: source

Thrown at resources/images/config.go:607

	if len(cfg.Meta.Fields) == 0 {
		// Default: include all fields except technical metadata.
		// Don't change this for no good reason. Please don't.
		cfg.Meta.Fields = []string{
			"! *{GPS,Exif,Exposure[MPB],Contrast,Resolution,Sharp,JPEG,Metering,Sensing,Saturation,ColorSpace,Flash,WhiteBalance}*",
		}
	}

	if len(cfg.Meta.Sources) == 0 {
		// Default to EXIF and IPTC (XMP is slower to decode).
		cfg.Meta.Sources = []string{"exif", "iptc"}
	} else {
		// Normalize to lowercase.
		for i, s := range cfg.Meta.Sources {
			cfg.Meta.Sources[i] = strings.ToLower(s)
			if !validMetaSources[cfg.Meta.Sources[i]] {
				keys := slices.Collect(maps.Keys(validMetaSources))
				slices.Sort(keys)
				return fmt.Errorf("invalid metadata source %q in imaging.meta.sources config; must be one of %s", s, keys)
			}
		}
	}

	return nil
}

type ExifConfig struct {
	// Regexp matching the Exif fields you want from the (massive) set of Exif info
	// available. As we cache this info to disk, this is for performance and
	// disk space reasons more than anything.
	// If you want it all, put ".*" in this config setting.
	// Note that if neither this or ExcludeFields is set, Hugo will return a small
	// default set.
	IncludeFields string

	// Regexp matching the Exif fields you want to exclude. This may be easier to use
	// than IncludeFields above, depending on what you want.

View on GitHub (pinned to 52c9bd7908)