gohugoio/hugo · error
failed to unmarshal XML: %w
Error message
failed to unmarshal XML: %w
What it means
When unmarshaling XML data (data files or transform.Unmarshal), Hugo parses the document into a map with mxj (xml.NewMapXml) and then asks for the root element name via xmlRoot.Root(). This error wraps the failure of that Root() call — the parsed result has no identifiable single root element, typically because the document is empty or malformed at the top level.
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 8a468df065)
Solutions
- Inspect the actual XML content being parsed (the wrapped mxj error and the file context in the error output identify it) and ensure it contains exactly one root element.
- For remote data, check the response before unmarshaling: verify `.Err` and status on the GetRemote resource and guard against empty `.Content`.
- Validate the file with an XML linter (`xmllint --noout file.xml`) and fix the malformation or re-download the truncated file.
Example fix
{{/* before */}}
{{ $data := resources.GetRemote $url | transform.Unmarshal }}
{{/* after */}}
{{ $r := resources.GetRemote $url }}
{{ if $r.Err }}{{ errorf "fetch failed: %s" $r.Err }}
{{ else if not $r.Content }}{{ errorf "empty response from %s" $url }}
{{ else }}{{ $data := $r | transform.Unmarshal }}{{ end }} Defensive patterns
Strategy: validation
Validate before calling
// Well-formedness check before handing XML to Hugo
d := xml.NewDecoder(bytes.NewReader(data))
for { if _, err := d.Token(); err == io.EOF { break } else if err != nil { return fmt.Errorf("bad XML: %w", err) } } Try / catch
if err != nil && strings.Contains(err.Error(), "failed to unmarshal XML") {
// wraps the parser error — inspect errors.Unwrap(err) for line/position info
} Prevention
- Validate feeds/XML with xmllint or encoding/xml in your ingest pipeline before placing them in data/
- Ensure correct encoding declarations and escaped entities (&) in generated XML
- For remote XML (resources.GetRemote), check the HTTP status and content-type before Unmarshal
When it happens
Trigger: Calling `transform.Unmarshal` on an XML string, or loading a `.xml` data file, where xml.NewMapXml succeeded (or returned partial data) but Root() errors — e.g. an empty document, only an XML declaration/comments with no element, or content that yields no root key in the map.
Common situations: Fetching a remote feed with `resources.GetRemote` that returns an empty body or an HTML error page and piping it into transform.Unmarshal; truncated XML files from failed downloads; data files accidentally saved empty.
Related errors
- XML root element '%s' has no value
- unmarshal failed: %w
- cannot unmarshal CSV into %T: expected at least a header row
- can't evaluate an invalid value
- unmarshal takes 1 or 2 arguments
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/32d333034a560765.json.
Report an issue: GitHub ↗.