{"record":{"id":"2bc6383fa3bfcb44","repo":"gohugoio/hugo","slug":"slice-of-untyped-nil","errorCode":null,"errorMessage":"slice of untyped nil","messagePattern":"slice of untyped nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/internal/go_templates/texttemplate/funcs.go","lineNumber":245,"sourceCode":"\t\t\t// the loop holds invariant: item.IsValid()\n\t\t\tpanic(\"unreachable\")\n\t\tdefault:\n\t\t\treturn reflect.Value{}, fmt.Errorf(\"can't index item of type %s\", item.Type())\n\t\t}\n\t}\n\treturn item, nil\n}\n\n// Slicing.\n\n// slice returns the result of slicing its first argument by the remaining\n// arguments. Thus \"slice x 1 2\" is, in Go syntax, x[1:2], while \"slice x\"\n// is x[:], \"slice x 1\" is x[1:], and \"slice x 1 2 3\" is x[1:2:3]. The first\n// argument must be a string, slice, or array.\nfunc slice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) {\n\titem = indirectInterface(item)\n\tif !item.IsValid() {\n\t\treturn reflect.Value{}, fmt.Errorf(\"slice of untyped nil\")\n\t}\n\tvar isNil bool\n\tif item, isNil = indirect(item); isNil {\n\t\treturn reflect.Value{}, fmt.Errorf(\"slice of nil pointer\")\n\t}\n\tif len(indexes) > 3 {\n\t\treturn reflect.Value{}, fmt.Errorf(\"too many slice indexes: %d\", len(indexes))\n\t}\n\tvar cap int\n\tswitch item.Kind() {\n\tcase reflect.String:\n\t\tif len(indexes) == 3 {\n\t\t\treturn reflect.Value{}, fmt.Errorf(\"cannot 3-index slice a string\")\n\t\t}\n\t\tcap = item.Len()\n\tcase reflect.Array, reflect.Slice:\n\t\tcap = item.Cap()\n\tdefault:","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/internal/go_templates/texttemplate/funcs.go#L227-L263","documentation":"Raised by the `slice` template action (`{{slice .X 1 2}}`) when the first argument is an untyped nil — i.e. `indirectInterface` returned an invalid reflect.Value (funcs.go:244-245). `slice` requires a string, slice, or array; nil satisfies none. Equivalent to calling Go's `s[1:2]` on a bare `var x interface{} = nil`.","triggerScenarios":"`{{slice .Missing 0 2}}` where `.Missing` is absent from the context and resolves to nil; `{{slice . 0 1}}` with a nil root; `{{slice (index .Map \"absent\") 0}}` because a missing map key yields the zero Value which becomes nil.","commonSituations":"Optional front matter field omitted on a page; shortcode param not provided and no default; map lookup that returns nothing; conditional content where the variable is only set in some branches.","solutions":["Wrap the slice in a nil guard: `{{with .MaybeSlice}}{{slice . 0 2}}{{end}}`.","Provide a default via `{{slice (or .MaybeSlice \"\") 0 2}}` or a default slice in Go.","Fix the data source so the field is always a populated string/slice.","Use `{{if .MaybeSlice}}...{{end}}` to skip the action entirely when absent."],"exampleFix":"// before\n{{slice .Excerpt 0 100}}   // .Excerpt is unset -> nil\n\n// after\n{{with .Excerpt}}{{slice . 0 100}}{{end}}","handlingStrategy":"validation","validationCode":"// In template, only slice when present and sliceable:\n//   {{with .MaybeSlice}}{{slice . 0 2}}{{end}}\n// Or in Go, never pass nil — pass \"\" or an empty slice.","typeGuard":"func isSliceableString(v interface{}) bool {\n    if v == nil { return false }\n    switch reflect.TypeOf(v).Kind() {\n    case reflect.String, reflect.Slice, reflect.Array:\n        return true\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Always provide defaults for optional fields used in slice actions.","Use {{with ...}} to skip the action when the value is absent.","Avoid passing `interface{}` resolved nils into templates; coerce at the data boundary."],"tags":["template","slice","nil","text-template"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}