gohugoio/hugo · error
cannot unmarshal CSV into %T
Error message
cannot unmarshal CSV into %T
What it means
Thrown by Decoder.unmarshalCSV (parser/metadecoders/decoder.go:377) in the default branch of the type switch when the target 'v' is not *interface{} (*any). The CSV decoder only knows how to populate a generic any (which it then sets to either a [][]string for slice mode or []map[string]string for map mode); passing a typed pointer like *[][]string or *MyStruct hits the default branch. The %T names the unsupported destination type.
Source
Thrown at parser/metadecoders/decoder.go:377
seen[fieldName] = true
}
sm := make([]map[string]string, len(records)-1)
for i, record := range records[1:] {
m := make(map[string]string, len(records[0]))
for j, col := range record {
m[records[0][j]] = col
}
sm[i] = m
}
*vv = sm
case "slice":
*vv = records
default:
return fmt.Errorf("cannot unmarshal CSV into %T: invalid targetType: expected either slice or map, received %s", v, d.TargetType)
}
default:
return fmt.Errorf("cannot unmarshal CSV into %T", v)
}
return nil
}
func parseORGDate(s string) string {
r := regexp.MustCompile(`[<\[](\d{4}-\d{2}-\d{2}) .*[>\]]`)
if m := r.FindStringSubmatch(s); m != nil {
return m[1]
}
return s
}
func (d Decoder) unmarshalORG(data []byte, v any) error {
config := org.New()
config.Log = log.Default() // TODO(bep)
document := config.Parse(bytes.NewReader(data), "")
if document.Error != nil {View on GitHub (pinned to 52c9bd7908)
Solutions
- Decode CSV into a *interface{} (var v any; dec.UnmarshalTo(data, metadecoders.CSV, &v)) then type-assert the result.
- For slice mode the result is [][]string; for map mode it is []map[string]string — assert accordingly.
- Use encoding/csv directly if you need a typed reader.
Example fix
// before var rows [][]string err := dec.UnmarshalTo(data, metadecoders.CSV, &rows) // after var v any err := dec.UnmarshalTo(data, metadecoders.CSV, &v) rows := v.([][]string)
Defensive patterns
Strategy: type-guard
Validate before calling
// CSV decoder only accepts *interface{}.
func isAnyPointer(v any) bool {
_, ok := v.(*any)
return ok
} Type guard
func isCSVTarget(v any) bool {
_, ok := v.(*any)
return ok
} Try / catch
var v any
if err := dec.UnmarshalTo(data, metadecoders.CSV, &v); err != nil {
return fmt.Errorf("CSV decode: %w", err)
}
// type-assert: v.([][]string) or v.([]map[string]string) Prevention
- Always decode CSV into *interface{} then type-assert the result.
- Use encoding/csv directly for typed/struct decoding.
- Remember CSV never populates typed structs.
When it happens
Trigger: Calling UnmarshalTo/Unmarshal for CSV with v = &rows where rows is [][]string, or v = &myStruct; programmatic misuse expecting reflection-based typed decoding that the CSV path does not support.
Common situations: Theme/plugin authors calling the metadecoders CSV path with a concrete typed destination; assuming CSV decodes into typed structs like JSON/TOML do.
Related errors
- unmarshal: %T not supported
- invalid targetType: expected either slice or map, received %
- cannot unmarshal CSV into %T: invalid targetType: expected e
- XML root element '%s' must be a map/object, got %T
- unmarshal of format %q is not supported
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/0fed5c1893f4cc36.
Report an issue: GitHub.