gohugoio/hugo · error
slice bounds out of range
Error message
slice bounds out of range
What it means
After parsing indices, `SliceString` validates them against the rune length of the string: the start must be in `[0, len)` and the end in `[0, len]`. Out-of-range or negative indices produce this error instead of a Go runtime panic, mirroring Go slice-bounds semantics on runes (not bytes).
Source
Thrown at tpl/strings/strings.go:346
if argNum > 0 {
if argStart, err = cast.ToIntE(startEnd[0]); err != nil {
return "", errors.New("start argument must be integer")
}
}
if argNum > 1 {
if argEnd, err = cast.ToIntE(startEnd[1]); err != nil {
return "", errors.New("end argument must be integer")
}
}
if argNum > 2 {
return "", errors.New("too many arguments")
}
asRunes := []rune(aStr)
if argNum > 0 && (argStart < 0 || argStart >= len(asRunes)) {
return "", errors.New("slice bounds out of range")
}
if argNum == 2 {
if argEnd < 0 || argEnd > len(asRunes) {
return "", errors.New("slice bounds out of range")
}
return string(asRunes[argStart:argEnd]), nil
} else if argNum == 1 {
return string(asRunes[argStart:]), nil
} else {
return string(asRunes[:]), nil
}
}
// Split slices an input string into all substrings separated by delimiter.
func (ns *Namespace) Split(a any, delimiter string) ([]string, error) {
aStr, err := cast.ToStringE(a)
if err != nil {View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Clamp or check the string length first: `{{ if gt (len $s) 3 }}{{ slicestr $s 0 3 }}{{ end }}`
- Use `substr` if you need negative/from-the-end indexing: `{{ substr $s -3 }}`
- Remember indices are rune-based; derive bounds from `strings.RuneCount` semantics (Hugo's `len` on strings counts runes) rather than byte offsets
Example fix
// before
{{ slicestr .Title 0 10 }} <!-- fails when .Title has < 10 runes -->
// after
{{ if ge (len .Title) 10 }}{{ slicestr .Title 0 10 }}{{ else }}{{ .Title }}{{ end }} Defensive patterns
Strategy: validation
Validate before calling
{{ $s := .s }}{{ $start := int .start }}{{ $end := int .end }}
{{ $n := len $s }}
{{ $start = math.Max (math.Min $start $n) (sub 0 $n) }}
{{ $end = math.Min $end $n }}
{{ if lt $start $end }}{{ substr $s $start $end }}{{ end }} Try / catch
{{ with try (substr .s .start .end) }}{{ if .Err }}{{ warnf "substr out of range for %q" .s }}{{ else }}{{ .Value }}{{ end }}{{ end }} Prevention
- Clamp start/end against `len $s` before calling substr, especially when indices are computed or come from content of varying length.
- Remember substr counts runes/bytes of the actual string — short titles or empty summaries are the usual trigger.
- Guard empty strings: `{{ if $s }}...{{ end }}` before slicing.
When it happens
Trigger: `{{ slicestr "abc" 5 }}` (start ≥ rune length), `{{ slicestr "abc" -1 }}` (negative start), `{{ slicestr "abc" 0 10 }}` (end > rune length), or start on an empty string.
Common situations: Slicing a title/summary shorter than the hard-coded index; computing indices from byte length (`len`) on multibyte UTF-8 text while `slicestr` counts runes; empty param strings on some pages; expecting Python-style negative indices, which `slicestr` doesn't support (use `substr` for negatives).
Related errors
- start argument must be integer
- end argument must be integer
- too many arguments
- too few arguments
- length argument must be an integer
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/cb550488d4006ef6.json.
Report an issue: GitHub ↗.