gohugoio/hugo · error
invalid character: %q
Error message
invalid character: %q
What it means
The `delimiter` or `comment` option passed to `transform.Unmarshal` for CSV decoding must be a single character (one rune). Hugo converts the option string to a rune in `stringToRune`; if the string contains more than one rune, this error is raised showing the offending value.
Source
Thrown at tpl/transform/unmarshal.go:216
}
func stringToRune(v any) (rune, error) {
s, err := cast.ToStringE(v)
if err != nil {
return 0, err
}
if len(s) == 0 {
return 0, nil
}
var r rune
for i, rr := range s {
if i == 0 {
r = rr
} else {
return 0, fmt.Errorf("invalid character: %q", v)
}
}
return r, nil
}
View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Use exactly one character: `dict "delimiter" ";"` or `dict "comment" "#"`.
- For tab-delimited data, pass a literal tab: `dict "delimiter" "\t"`.
- Pre-process the data (e.g. `replace`) if the source truly uses a multi-character separator.
Example fix
{{/* before */}}
{{ $d := transform.Unmarshal (dict "delimiter" "::") $csv }}
{{/* after */}}
{{ $csv = replace $csv "::" ";" }}
{{ $d := transform.Unmarshal (dict "delimiter" ";") $csv }} Defensive patterns
Strategy: validation
Validate before calling
{{/* CSV delimiter option must be exactly one character */}}
{{ $opts := dict "delimiter" ";" }}
{{ if ne (len $opts.delimiter) 1 }}{{ errorf "delimiter must be a single character" }}{{ end }}
{{ $data := transform.Unmarshal $opts $csv }} Try / catch
{{ with try (transform.Unmarshal (dict "delimiter" $d "comment" $c) $csv) }}
{{ with .Err }}{{ errorf "bad CSV option: %s" . }}{{ end }}
{{ end }} Prevention
- Pass CSV options 'delimiter' and 'comment' as single-character strings only (e.g. "," or ";"), never multi-character or empty strings
- Keep option values literal in the template rather than computed from user data
- Consult the transform.Unmarshal docs for the exact option map shape before using CSV options
When it happens
Trigger: Calling `transform.Unmarshal (dict "delimiter" ";;") $csv` or `dict "comment" "//"` — any Delimiter/Comment option string longer than one character.
Common situations: Trying to use multi-character delimiters ("::", "\t " with stray whitespace), using "//" as a comment prefix out of habit from code, or accidentally passing an escaped sequence that expands to multiple characters.
Related errors
- failed to decode options: %w
- invalid options type: %w
- unmarshal takes 1 or 2 arguments
- first argument must be a map
- format %q not supported
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/e6955b5325ef8ff4.json.
Report an issue: GitHub ↗.