gohugoio/hugo · error

invalid dictionary key

Error message

invalid dictionary key

What it means

Keys in `dict` must be either a string or a []string (a string slice creates nested maps). Any other key type — int, bool, Page, nil, or an untyped param — fails this switch and aborts the dictionary construction.

Source

Thrown at tpl/collections/collections.go:188

		switch v := values[i].(type) {
		case string:
			key = v
		case []string:
			for i := range len(v) - 1 {
				key = v[i]
				var m map[string]any
				v, found := dict[key]
				if found {
					m = v.(map[string]any)
				} else {
					m = make(map[string]any)
					dict[key] = m
				}
				dict = m
			}
			key = v[len(v)-1]
		default:
			return nil, errors.New("invalid dictionary key")
		}
		dict[key] = values[i+1]
	}

	return root, nil
}

// First returns the first limit items in list l.
func (ns *Namespace) First(limit any, l any) (any, error) {
	if limit == nil || l == nil {
		return nil, errors.New("both limit and seq must be provided")
	}

	limitv, err := cast.ToIntE(limit)
	if err != nil {
		return nil, err
	}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Convert the key to a string first: `{{ dict (string $i) $v }}` or `{{ dict (printf "%d" $i) $v }}`.
  2. Quote key values in front matter/config so YAML keeps them strings.
  3. For nested keys, ensure the slice contains only strings: `{{ dict (slice "outer" "inner") $v }}`.

Example fix

<!-- before -->
{{ $m := dict $i .Title }}
<!-- after -->
{{ $m := dict (string $i) .Title }}
Defensive patterns

Strategy: type-guard

Validate before calling

{{ $k := .Params.key | default "" }}
{{ if ne (printf "%T" $k) "string" }}{{ $k = printf "%v" $k }}{{ end }}
{{ $d := dict $k "value" }}

Type guard

{{/* coerce keys to strings before dict */}}
{{ $key := printf "%v" $rawKey }}

Prevention

When it happens

Trigger: `{{ dict 1 "one" }}`, `{{ dict .Params.someKey "v" }}` where the param is not a string, or `{{ dict (slice 1 2) "v" }}` (an []int slice, not []string).

Common situations: Using loop indices or integers as keys, passing front-matter params that YAML parsed as numbers/booleans (e.g. an unquoted `2024` or `true`), or expecting `slice "a" "b"` nesting to work with non-string elements.

Related errors


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