gohugoio/hugo · error

failed to unmarshal XML

Error message

failed to unmarshal XML: %w

What it means

Thrown in the XML branch of Decoder.UnmarshalTo (parser/metadecoders/decoder.go:294) when xmlRoot.Root() (from clbanning/mxj) returns an error after parsing. This means the XML could be parsed into a Map but extracting the root element name failed — typically indicating malformed, empty, or non-element XML. The error is wrapped via toFileError to attach file context.

Solutions

  1. Validate the XML is well-formed with a single root element using xmllint or a browser.
  2. Ensure the file has actual element content, not just an <?xml?> declaration.
  3. Re-fetch or re-export the source XML if it is truncated/corrupt.
  4. If the data isn't truly XML, rename/reformat the file to its actual format.

Example fix

<!-- before -->
<?xml version="1.0"?>

<!-- after -->
<?xml version="1.0"?>
<root>
  <item>value</item>
</root>
Defensive patterns

Strategy: validation

Validate before calling

// Basic XML well-formedness check before UnmarshalTo.
func looksLikeXML(data []byte) bool {
    return bytes.Contains(data, []byte("<")) && bytes.Contains(data, []byte(">"))
}

Try / catch

if err := dec.UnmarshalTo(data, metadecoders.XML, &m); err != nil {
    return fmt.Errorf("invalid XML data: %w", err)
}

Prevention

When it happens

Trigger: Feeding UnmarshalTo XML data that is a fragment, an XML declaration only (<?xml?> with no root element), mixed/invalid content, or text that mxj parses but cannot identify a single root for; loading a .xml data file with broken structure.

Common situations: An XML data file under data/ that is empty or contains only a declaration; an externally-fetched XML feed that is malformed; copy-paste introducing a stray tag; encoding issues (BOM, wrong charset) confusing the parser.

Related errors


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

Appendix: source

Thrown at parser/metadecoders/decoder.go:294

// UnmarshalTo unmarshals data in format f into v.
func (d Decoder) UnmarshalTo(data []byte, f Format, v any) error {
	var err error

	switch f {
	case ORG:
		err = d.unmarshalORG(data, v)
	case JSON:
		err = json.Unmarshal(data, v)
	case XML:
		var xmlRoot xml.Map
		xmlRoot, err = xml.NewMapXml(data)

		var xmlValue map[string]any
		if err == nil {
			xmlRootName, err := xmlRoot.Root()
			if err != nil {
				return toFileError(f, data, fmt.Errorf("failed to unmarshal XML: %w", err))
			}

			// Get the root value and verify it's a map
			rootValue := xmlRoot[xmlRootName]
			if rootValue == nil {
				return toFileError(f, data, fmt.Errorf("XML root element '%s' has no value", xmlRootName))
			}

			// Type check before conversion
			mapValue, ok := rootValue.(map[string]any)
			if !ok {
				return toFileError(f, data, fmt.Errorf("XML root element '%s' must be a map/object, got %T", xmlRootName, rootValue))
			}
			xmlValue = mapValue
		}

		switch v := v.(type) {
		case *map[string]any:

View on GitHub (pinned to 52c9bd7908)