{"record":{"id":"d6b484bacb6ffa5d","repo":"gohugoio/hugo","slug":"cannot-3-index-slice-a-string","errorCode":null,"errorMessage":"cannot 3-index slice a string","messagePattern":"cannot 3-index slice a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/internal/go_templates/texttemplate/funcs.go","lineNumber":258,"sourceCode":"// 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:\n\t\treturn reflect.Value{}, fmt.Errorf(\"can't slice item of type %s\", item.Type())\n\t}\n\t// set default values for cases item[:], item[i:].\n\tidx := [3]int{0, item.Len()}\n\tfor i, index := range indexes {\n\t\tx, err := indexArg(index, cap)\n\t\tif err != nil {\n\t\t\treturn reflect.Value{}, err\n\t\t}\n\t\tidx[i] = x\n\t}\n\t// given item[i:j], make sure i <= j.\n\tif idx[0] > idx[1] {","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/internal/go_templates/texttemplate/funcs.go#L240-L276","documentation":"Raised by the `slice` action specifically when the item is a string and exactly three indexes are given (funcs.go:257-258). Go does not permit 3-index slice expressions on strings (`s[i:j:k]` is only valid for arrays/slices), so the template engine rejects it up front.","triggerScenarios":"`{{slice .Str 0 2 4}}` where `.Str` is a string; trying to bound a string sub-slice's capacity like an array.","commonSituations":"Treating a string like a `[]byte`; helper template written for slices being reused on strings; front matter that changed from a byte slice to a string.","solutions":["Drop the third index for strings: `{{slice .Str 0 2}}`.","If you really need cap-bounded slicing, convert to `[]byte`/`[]rune` in Go first."],"exampleFix":"// before\n{{slice .Name 0 3 5}}   // .Name is string\n\n// after\n{{slice .Name 0 3}}","handlingStrategy":"validation","validationCode":"// For strings, use at most 2 indexes:\n//   {{slice .Str 0 3}}\n// If you need cap semantics, operate on []byte in Go first.","typeGuard":"func isString(v interface{}) bool {\n    return v != nil && reflect.TypeOf(v).Kind() == reflect.String\n}","tryCatchPattern":null,"preventionTips":["Never pass 3 indexes to slice when the operand is a string.","Distinguish string from []byte at the data boundary."],"tags":["template","slice","string","text-template"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}