{"id":"9da14e1c327badf7","repo":"gohugoio/hugo","slug":"invalid-dictionary-key","errorCode":null,"errorMessage":"invalid dictionary key","messagePattern":"invalid dictionary key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/collections/collections.go","lineNumber":188,"sourceCode":"\t\tswitch v := values[i].(type) {\n\t\tcase string:\n\t\t\tkey = v\n\t\tcase []string:\n\t\t\tfor i := range len(v) - 1 {\n\t\t\t\tkey = v[i]\n\t\t\t\tvar m map[string]any\n\t\t\t\tv, found := dict[key]\n\t\t\t\tif found {\n\t\t\t\t\tm = v.(map[string]any)\n\t\t\t\t} else {\n\t\t\t\t\tm = make(map[string]any)\n\t\t\t\t\tdict[key] = m\n\t\t\t\t}\n\t\t\t\tdict = m\n\t\t\t}\n\t\t\tkey = v[len(v)-1]\n\t\tdefault:\n\t\t\treturn nil, errors.New(\"invalid dictionary key\")\n\t\t}\n\t\tdict[key] = values[i+1]\n\t}\n\n\treturn root, nil\n}\n\n// First returns the first limit items in list l.\nfunc (ns *Namespace) First(limit any, l any) (any, error) {\n\tif limit == nil || l == nil {\n\t\treturn nil, errors.New(\"both limit and seq must be provided\")\n\t}\n\n\tlimitv, err := cast.ToIntE(limit)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/collections/collections.go#L170-L206","documentation":"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.","triggerScenarios":"`{{ 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).","commonSituations":"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.","solutions":["Convert the key to a string first: `{{ dict (string $i) $v }}` or `{{ dict (printf \"%d\" $i) $v }}`.","Quote key values in front matter/config so YAML keeps them strings.","For nested keys, ensure the slice contains only strings: `{{ dict (slice \"outer\" \"inner\") $v }}`."],"exampleFix":"<!-- before -->\n{{ $m := dict $i .Title }}\n<!-- after -->\n{{ $m := dict (string $i) .Title }}","handlingStrategy":"type-guard","validationCode":"{{ $k := .Params.key | default \"\" }}\n{{ if ne (printf \"%T\" $k) \"string\" }}{{ $k = printf \"%v\" $k }}{{ end }}\n{{ $d := dict $k \"value\" }}","typeGuard":"{{/* coerce keys to strings before dict */}}\n{{ $key := printf \"%v\" $rawKey }}","tryCatchPattern":null,"preventionTips":["Use only strings (or string slices for nested keys) as dict keys","Coerce dynamic keys with `printf \"%v\"` or `string` before passing to dict","Watch for front-matter values parsed as ints/bools when you expected strings (e.g. unquoted YAML)"],"tags":["hugo","templates","dict","type-error","collections"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}