gohugoio/hugo · error
XML root element ' ' has no value
Error message
XML root element '%s' has no value
What it means
Thrown in the XML branch of Decoder.UnmarshalTo (parser/metadecoders/decoder.go:300) when the XML root element parses successfully and has a name, but its value is nil (the root element is empty or self-closing with no children/text). Hugo requires the root to carry a value, so an empty <root/> triggers this error naming the element.
Solutions
- Populate the root element with at least one child element or text value.
- If empty data is legitimate, handle the empty-data case upstream before feeding it to Hugo.
- Switch to JSON if the XML is always a flat key/value container.
Example fix
<!-- before --> <root/> <!-- after --> <root> <item>value</item> </root>
Defensive patterns
Strategy: validation
Validate before calling
// Ensure XML root has content before decoding.
func xmlRootHasContent(data []byte) bool {
return bytes.Count(data, []byte("<")) > 1
} Try / catch
if err := dec.UnmarshalTo(data, metadecoders.XML, &m); err != nil {
return fmt.Errorf("empty XML root: %w", err)
} Prevention
- Populate root element with child elements or text.
- Handle empty datasets upstream.
- Use JSON for simple flat containers.
When it happens
Trigger: An XML data file whose root element is empty: <root></root> or <root/>; an element that contains only attributes but no child elements or text, which mxj represents as nil.
Common situations: Exporting an empty dataset to XML; a feed endpoint returning an empty container; a templating tool emitting a self-closing root.
Related errors
- failed to unmarshal XML
- XML root element ' ' must be a map/object, got %T
- cannot unmarshal CSV into %T: expected at least a header…
- cannot unmarshal CSV into %T: header row contains duplicate…
- unmarshal failed
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/b597432e97864382.
Report an issue: GitHub.
Appendix: source
Thrown at parser/metadecoders/decoder.go:300
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:
*v = xmlValue
case *any:
*v = xmlValue
}
case TOML:
err = toml.Unmarshal(data, v)View on GitHub (pinned to 52c9bd7908)