gohugoio/hugo · error
startLevel: %w
Error message
startLevel: %w
What it means
`Fragments.ToHTML` renders a page's table of contents and accepts `startLevel` as `any`, casting it to int with `cast.ToIntE`. If the value passed from a template can't be coerced to an integer, the cast error is wrapped with the "startLevel:" prefix and the TableOfContents rendering fails for that page.
Source
Thrown at markup/tableofcontents/tableofcontents.go:158
for i := 1; i < level; i++ {
if len(heading.Headings) == 0 {
heading.Headings = append(heading.Headings, &Heading{})
}
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
}
View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Pass a plain integer (or numeric string) for startLevel, e.g. 2 — heading level numbers, not "h2".
- Check markup.tableOfContents.startLevel in site config is an integer between 1 and 6.
- Trace the theme partial/front-matter value feeding the ToC call and coerce or default it before rendering.
Example fix
# before (hugo.toml) [markup.tableOfContents] startLevel = "h2" # after [markup.tableOfContents] startLevel = 2
Defensive patterns
Strategy: validation
Validate before calling
if toc.StartLevel < 1 || toc.StartLevel > 6 {
return fmt.Errorf("markup.tableOfContents.startLevel must be 1-6, got %d", toc.StartLevel)
}
if toc.EndLevel != -1 && toc.EndLevel < toc.StartLevel {
return fmt.Errorf("endLevel %d must be >= startLevel %d", toc.EndLevel, toc.StartLevel)
} Prevention
- Keep markup.tableOfContents.startLevel within heading range (1-6) and as an integer, not a string
- Ensure endLevel >= startLevel when both are set
- Validate config changes with a local `hugo` build before committing
When it happens
Trigger: A template calling the internal ToC rendering (e.g. via `.TableOfContents` machinery or a partial passing options) with a non-numeric startLevel — a string like "h2", a map, nil-ish template value, or a misconfigured `markup.tableOfContents.startLevel` fed through as a non-castable type.
Common situations: Setting `markup.tableOfContents.startLevel` to a non-numeric string in site config; theme partials that compute the level from front matter and pass an empty or malformed value; confusing heading names ("h2") with numeric levels (2).
Related errors
- config needs a filename
- unable to cast %#v of type %T to []map[string]interface{}
- language %q not found
- unsupported format: %q
- failed to create workingDir: %w
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/f986e8ff26d2a15e.json.
Report an issue: GitHub ↗.