gohugoio/hugo · error
modulo operator can't be used with non integer value
Error message
modulo operator can't be used with non integer value
What it means
Hugo's `math.Mod` template function (Namespace.Mod in tpl/math/math.go) casts both operands to int64 via `cast.ToInt64E` before computing `n1 % n2`. If either cast fails — because the value is a non-numeric string, a nil, a map/slice, or otherwise not integer-convertible — Hugo returns this error instead of guessing. `ModBool` delegates to `Mod`, so it surfaces the same error.
Source
Thrown at tpl/math/math.go:166
}
// MaxInt64 returns the maximum value for a signed 64-bit integer.
func (ns *Namespace) MaxInt64() int64 {
return math.MaxInt64
}
// Min returns the smaller of all numbers in inputs. Any slices in inputs are flattened.
func (ns *Namespace) Min(inputs ...any) (minimum float64, err error) {
return ns.applyOpToScalarsOrSlices("Min", math.Min, inputs...)
}
// Mod returns n1 % n2.
func (ns *Namespace) Mod(n1, n2 any) (int64, error) {
ai, erra := cast.ToInt64E(n1)
bi, errb := cast.ToInt64E(n2)
if erra != nil || errb != nil {
return 0, errors.New("modulo operator can't be used with non integer value")
}
if bi == 0 {
return 0, errors.New("the number can't be divided by zero at modulo operation")
}
return ai % bi, nil
}
// ModBool returns the boolean of n1 % n2. If n1 % n2 == 0, return true.
func (ns *Namespace) ModBool(n1, n2 any) (bool, error) {
res, err := ns.Mod(n1, n2)
if err != nil {
return false, err
}
return res == int64(0), nil
}View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Ensure both operands are numeric: convert with `{{ mod (int $a) (int $b) }}` when the value may be a string.
- Guard against missing params: `{{ with .Params.count }}{{ mod (int .) 2 }}{{ end }}` or provide a default: `{{ mod (int (default 0 .Params.count)) 2 }}`.
- Fix the data source so numbers are unquoted in JSON/YAML/TOML front matter.
- When using shortcode params (`.Get`), remember they are strings — always wrap in `int`.
Example fix
<!-- before -->
{{ if modBool .Params.rank 2 }}even{{ end }}
<!-- after -->
{{ if modBool (int (default 0 .Params.rank)) 2 }}even{{ end }} Defensive patterns
Strategy: type-guard
Validate before calling
{{ if and (eq (printf "%T" $a) "int") (eq (printf "%T" $b) "int") }}{{ mod $a $b }}{{ end }} Type guard
// Go caller side
func isInt(v any) bool {
switch v.(type) {
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return true
}
return false
} Try / catch
if _, err := ns.ModBool(a, b); err != nil {
// fall back or surface template error
} Prevention
- Cast operands with `int` before calling `mod` in templates: `{{ mod (int $a) (int $b) }}`
- Remember front matter numbers parse as float64 in YAML/TOML — convert explicitly
- Never pass strings from .Params directly into math functions
When it happens
Trigger: Calling `{{ mod A B }}` or `{{ modBool A B }}` in a Hugo template where A or B is not convertible to int64: e.g. `{{ mod "abc" 3 }}`, `{{ mod .Params.count 2 }}` when the front-matter param is missing (nil) or a string like "10px", or passing a float string that cast.ToInt64E rejects. Note spf13/cast truncates plain floats, so the usual trigger is strings/nil, not floats.
Common situations: Alternating row striping with `{{ if modBool $index 2 }}` where the index variable is accidentally a string from a data file (JSON/YAML numbers quoted); front-matter params typed as strings; `.Params` lookups that return nil because the key is absent or cased differently; values piped from `printf` or `.Get` shortcode params which are always strings.
Related errors
- Pow operator can't be used with non-float value
- Round operator can't be used with non-float value
- Sqrt operator can't be used with non integer or float value
- 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/96de9876990450ea.json.
Report an issue: GitHub ↗.