gohugoio/hugo · error
length argument must be an integer
Error message
length argument must be an integer
What it means
When `substr` is called with two extra arguments (start and length), the length is converted with `cast.ToIntE`. A length value that can't be cast to an int — a non-numeric string, map, slice, or nil — produces this error, since Hugo won't guess how many characters you meant.
Source
Thrown at tpl/strings/strings.go:407
asRunes := []rune(s)
rlen := len(asRunes)
var start, length int
switch len(nums) {
case 0:
return "", errors.New("too few arguments")
case 1:
if start, err = cast.ToIntE(nums[0]); err != nil {
return "", errors.New("start argument must be an integer")
}
length = rlen
case 2:
if start, err = cast.ToIntE(nums[0]); err != nil {
return "", errors.New("start argument must be an integer")
}
if length, err = cast.ToIntE(nums[1]); err != nil {
return "", errors.New("length argument must be an integer")
}
default:
return "", errors.New("too many arguments")
}
if rlen == 0 {
return "", nil
}
if start < 0 {
start += rlen
}
// start was originally negative beyond rlen
if start < 0 {
start = 0
}
View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Pass an integer length: `{{ substr $s 0 30 }}`
- Coerce config/param values: `{{ substr $s 0 (int site.Params.summaryLength) }}`
- Guard optional params with `with`/`default`: `{{ substr $s 0 (default 30 .Params.len) }}`
Example fix
// before
{{ substr .Summary 0 .Params.len }} <!-- len: "thirty" -->
// after
{{ substr .Summary 0 (int (default 30 .Params.len)) }} Defensive patterns
Strategy: type-guard
Validate before calling
{{ $n := int (default 70 site.Params.truncateLen) }}
{{ truncate $n .Summary }} Type guard
{{ $n := .Params.maxLen }}{{ if $n }}{{ $n = int $n }}{{ else }}{{ $n = 70 }}{{ end }} Try / catch
{{ with try (truncate .n .text) }}{{ if .Err }}{{ warnf "%s" .Err }}{{ .text }}{{ else }}{{ .Value }}{{ end }}{{ end }} Prevention
- Cast config-sourced lengths with `int` — TOML gives int64, YAML strings stay strings, JSON numbers arrive as float64.
- Keep the length as a literal in the template when it doesn't need to be configurable.
- Normalize all numeric params once in a partial and pass typed values downward.
When it happens
Trigger: `{{ substr "hello" 0 "abc" }}`, or a length sourced from `.Params`/`site.Params` that holds a non-numeric string or is unset on some pages.
Common situations: Summary-length settings stored as strings like "30 chars" in config; passing the ellipsis or suffix string in the length position; nil front-matter fields on a subset of pages breaking only those builds.
Related errors
- start argument must be integer
- end argument must be integer
- symdiff: failed to convert value: %w
- the math.Abs function requires a numeric argument
- Ceil operator can't be used with non-float value
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/d98e06eef1d96908.json.
Report an issue: GitHub ↗.