Somebody set us up the BOM, or UTF-8 byte-order marker corrupts CSV maps

I’m unmarshalling a CSV file and iterating over the contents. The fragment of code looks like:

{{ $data := dict }}
{{ with $file := .datafile }}
    {{ with resources.Get (printf "people/writing/%s" $file) }}
        {{ $opts := dict "targetType" "map" }}
        {{ $data = transform.Unmarshal $opts . }}
    {{ end }}

    {{ range $data }}
<p>Title = {{ index . "title" }}</p>
<p>Author = {{ index . "author" }}</p>
    {{ end }}
{{ end }}

and the input data looks something like:

title,author
"Wuthering Heights","Thomas Pynchon"

Retrieving author works without problems. However, {{ index . "title" }} returns an empty string every time.

If I use debug.Dump the correct value for title is shown, so it appears to be present in the map. It just doesn’t seem to be accessible via index.

My first assumption was that there was something special about title. However, if I change the name of the first column (and update the code accordingly) the same thing happens. It seems that whatever the name of the first column, it cannot be used to retrieve values from the map.

Here’s a fragment of sample output:

{ "author": "Thomas Pynchon", "title": "Wuthering Heights" } 
Title = 
Author = Thomas Pynchon

with the first line produced by debug.Dump to demonstrate that title is present in the map.

I finally figured out that the file is UTF-8 with a byte-order marker (BOM) at the start. What I believe is happening is that the BOM is not being skipped, but is included in the first column name, i.e. instead of the map having “title” as the name of the first column, it has “title”.

Resaving the file as regular UTF-8 (no BOM) resolved the issue.

This is awkward, because Excel’s default CSV export format is now UTF-8 CSV, which includes the BOM. If you use the alternative “.csv” export format (which produces a file with no BOM), then Hugo can parse it without problems.

There seems to be a Go issue that may be related to this, but it doesn’t look as if it was ever fixed.

Is there a way to tell Hugo to discard the BOM when parsing CSVs, or could this be fixed in a future release?

Version: hugo v0.166.0+extended+withdeploy darwin/arm64 BuildDate=2026-09-09T14:45:02Z VendorInfo=Homebrew

This works with CSV files with or without a leading BOM.

{{ $data := dict }}
{{ with resources.Get "a.csv" }}
	{{ $opts := dict "targetType" "map" "format" "csv" }}
	{{ $content := .Content | strings.TrimPrefix "\xef\xbb\xbf" }}
	{{ $data = transform.Unmarshal $opts $content }}
{{ end }}

{{ range $data }}
	<p>Title = {{ .title }}</p>
	<p>Author = {{ .author }}</p>
{{ end }}

But note that unmarshaling a resource is more efficient than unmarshaling a resource’s .Content, particularly with larger files.

See: