gohugoio/hugo · error

unable to cast %#v of type %T to []map[string]interface{}

Error message

unable to cast %#v of type %T to []map[string]interface{}

What it means

hmaps.ToSliceStringMap converts an arbitrary value into []map[string]interface{}. It accepts []map[string]any, Params, map[string]any, or []any (keeping only map entries); every other concrete type hits the default case and returns this error with the value and its Go type. Hugo uses it where config or front matter must be a list of maps (e.g. lists of objects).

Source

Thrown at common/hmaps/maps.go:112

// ToSliceStringMap converts in to []map[string]interface{}.
func ToSliceStringMap(in any) ([]map[string]any, error) {
	switch v := in.(type) {
	case []map[string]any:
		return v, nil
	case Params:
		return []map[string]any{v}, nil
	case map[string]any:
		return []map[string]any{v}, nil
	case []any:
		var s []map[string]any
		for _, entry := range v {
			if vv, ok := entry.(map[string]any); ok {
				s = append(s, vv)
			}
		}
		return s, nil
	default:
		return nil, fmt.Errorf("unable to cast %#v of type %T to []map[string]interface{}", in, in)
	}
}

// LookupEqualFold finds key in m with case insensitive equality checks.
func LookupEqualFold[T any | string](m map[string]T, key string) (T, string, bool) {
	if v, found := m[key]; found {
		return v, key, true
	}
	for k, v := range m {
		if strings.EqualFold(k, key) {
			return v, k, true
		}
	}
	var s T
	return s, "", false
}

// MergeShallow merges src into dst, but only if the key does not already exist in dst.

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Fix the config/front matter shape: the value must be a list of maps (in YAML, a list of `key: value` blocks; in TOML, [[double-bracket]] tables).
  2. Print the offending value with `printf "%#v"` or `jsonify` to see its actual type, then adjust the source.
  3. If keys are non-string (old YAML), quote them or restructure so YAML produces map[string]interface{}.

Example fix

# before (front matter)
resources: ["image.png", "doc.pdf"]

# after
resources:
  - src: image.png
  - src: doc.pdf
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the value is a slice of maps before conversion
if _, ok := v.([]map[string]any); !ok {
	if s, ok := v.([]any); ok { /* check each element is a map */ _ = s }
}

Type guard

func isSliceOfMaps(v any) bool {
	switch s := v.(type) {
	case []map[string]any:
		return true
	case []any:
		for _, e := range s {
			if _, ok := e.(map[string]any); !ok { return false }
		}
		return len(s) > 0
	}
	return false
}

Try / catch

m, err := hmaps.ToSliceStringMap(v)
if err != nil {
	// value isn't a list of objects — report the actual type from the error
}

Prevention

When it happens

Trigger: Passing a scalar, a string, a []string, or a map with non-string keys where Hugo expects a slice of maps — e.g. a config section like `[[menu.main]]`-style structures, cascade lists, or template code calling this converter on a param that is a plain string or list of strings.

Common situations: YAML/TOML front matter or site config where a key expected to be a list of objects is written as a plain list of strings or a single string (e.g. `outputs` vs a list of tables); data files with map[interface{}]interface{} keys from older YAML; passing the wrong param to a function expecting map slices.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/5f99470d0f4648f2.json. Report an issue: GitHub ↗.