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

  1. Pass a plain integer (or numeric string) for startLevel, e.g. 2 — heading level numbers, not "h2".
  2. Check markup.tableOfContents.startLevel in site config is an integer between 1 and 6.
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/f986e8ff26d2a15e.json. Report an issue: GitHub ↗.