gohugoio/hugo · error
stopLevel: %w
Error message
stopLevel: %w
What it means
Returned by Fragments.ToHTML when cast.ToIntE(stopLevel) fails: the stopLevel argument is not convertible to int. Identical shape to the startLevel error but for the stop/end level. Uses %w so the underlying cast error is unwrappable. Note a stopLevel of -1 is a valid sentinel meaning 'include all'.
Source
Thrown at markup/tableofcontents/tableofcontents.go:163
heading = heading.Headings[len(heading.Headings)-1]
}
heading.Headings = append(heading.Headings, h)
}
// ToHTML renders the ToC as HTML.
func (toc *Fragments) ToHTML(startLevel, stopLevel any, ordered bool) (template.HTML, error) {
if toc == nil {
return "", nil
}
iStartLevel, err := cast.ToIntE(startLevel)
if err != nil {
return "", fmt.Errorf("startLevel: %w", err)
}
iStopLevel, err := cast.ToIntE(stopLevel)
if err != nil {
return "", fmt.Errorf("stopLevel: %w", err)
}
b := &tocBuilder{
s: strings.Builder{},
h: toc.Headings,
startLevel: iStartLevel,
stopLevel: iStopLevel,
ordered: ordered,
}
b.Build()
return template.HTML(b.s.String()), nil
}
func (toc Fragments) walk(fn func(*Heading)) {
for _, h := range toc.Headings {
h.walk(fn)
}
}View on GitHub (pinned to 52c9bd7908)
Solutions
- Pass stopLevel as an int (use -1 to include all levels).
- Coerce the source value: {{ $stop := int (.Params.stop_level | default 3) }}.
- Double-check argument order: ToHTML(startLevel, stopLevel, ordered).
Example fix
{{/* before */}}
{{ .Fragments.ToHTML 2 .Params.end_level true }}
{{/* after */}}
{{ .Fragments.ToHTML 2 (int (.Params.end_level | default 3)) true }} Defensive patterns
Strategy: type-guard
Validate before calling
// Coerce stopLevel; -1 means include all.
func toStopLevel(v any, def int) int {
switch t := v.(type) {
case int:
return t
case float64:
return int(t)
case string:
if n, err := strconv.Atoi(t); err == nil {
return n
}
}
return def
} Type guard
func isIntLike(v any) bool {
switch v.(type) {
case int, int64, float64:
return true
case string:
_, err := strconv.Atoi(v.(string))
return err == nil
}
return false
} Try / catch
html, err := frags.ToHTML(start, stop, ordered)
if err != nil && strings.HasPrefix(err.Error(), "stopLevel:") {
html, err = frags.ToHTML(start, 3, ordered)
} Prevention
- Pass stopLevel as int; use -1 deliberately to include all levels.
- Keep argument order straight: (startLevel, stopLevel, ordered).
- Coerce front-matter strings in the template.
When it happens
Trigger: Calling Fragments.ToHTML(start, stop, ordered) with stop set to a non-integer string, e.g. ToHTML(2, "3", false) where "3" is a string, or ToHTML(2, nil, false). Fires from a layout partial rendering the ToC.
Common situations: Template params read from front matter/section params as strings; a partial receiving a shortcode arg as string; passing .Site.Params.toc.end_level without conversion; accidentally passing an ordered bool into the stopLevel slot.
Related errors
- startLevel: %w
- size of result exceeds limit
- invalid arguments to Seq
- 'increment' must not be 0
- 'increment' must be > 0
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/b0ea1be541507acd.
Report an issue: GitHub.