{"record":{"id":"f986e8ff26d2a15e","repo":"gohugoio/hugo","slug":"startlevel-w","errorCode":null,"errorMessage":"startLevel: %w","messagePattern":"startLevel: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"markup/tableofcontents/tableofcontents.go","lineNumber":158,"sourceCode":"\n\tfor i := 1; i < level; i++ {\n\t\tif len(heading.Headings) == 0 {\n\t\t\theading.Headings = append(heading.Headings, &Heading{})\n\t\t}\n\t\theading = heading.Headings[len(heading.Headings)-1]\n\t}\n\theading.Headings = append(heading.Headings, h)\n}\n\n// ToHTML renders the ToC as HTML.\nfunc (toc *Fragments) ToHTML(startLevel, stopLevel any, ordered bool) (template.HTML, error) {\n\tif toc == nil {\n\t\treturn \"\", nil\n\t}\n\n\tiStartLevel, err := cast.ToIntE(startLevel)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"startLevel: %w\", err)\n\t}\n\n\tiStopLevel, err := cast.ToIntE(stopLevel)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"stopLevel: %w\", err)\n\t}\n\n\tb := &tocBuilder{\n\t\ts:          strings.Builder{},\n\t\th:          toc.Headings,\n\t\tstartLevel: iStartLevel,\n\t\tstopLevel:  iStopLevel,\n\t\tordered:    ordered,\n\t}\n\tb.Build()\n\treturn template.HTML(b.s.String()), nil\n}\n","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/markup/tableofcontents/tableofcontents.go#L140-L176","documentation":"Returned by Fragments.ToHTML when cast.ToIntE(startLevel) fails, i.e. the startLevel argument cannot be converted to an int. startLevel is typed as `any`, so template authors can pass a non-numeric string and trigger this. The error wraps the cast error with %w so the original cast error is unwrappable.","triggerScenarios":"Calling .Fragments.ToHTML(...) (or the {{.TableOfContents}}-style path) with startLevel set to a non-integer string, e.g. ToHTML(\"abc\", 3, false). Common via a template that interpolates a config value as a string instead of an int.","commonSituations":"A custom layout passing .Site.Params.toc.start_level (a string from front matter) straight into ToHTML; partials that receive parameters from a shortcode as strings; upgrading where an old template passed a stringified level.","solutions":["Pass startLevel as an int literal or cast it: ToHTML (intVal, stop, ordered).","If the value comes from params, coerce with int or a default: {{ $start := int (.Params.start_level | default 2) }}.","Validate the param type in your template before calling ToHTML."],"exampleFix":"{{/* before */}}\n{{ .Fragments.ToHTML .Params.start_level .Params.stop_level false }}\n\n{{/* after */}}\n{{ .Fragments.ToHTML (int (.Params.start_level | default 2)) (int (.Params.stop_level | default 3)) false }}","handlingStrategy":"type-guard","validationCode":"// Coerce before calling ToHTML.\nfunc toIntLevel(v any, def int) int {\n    switch t := v.(type) {\n    case int:\n        return t\n    case float64:\n        return int(t)\n    case string:\n        if n, err := strconv.Atoi(t); err == nil {\n            return n\n        }\n    }\n    return def\n}","typeGuard":"func isIntLike(v any) bool {\n    switch v.(type) {\n    case int, int64, float64:\n        return true\n    case string:\n        _, err := strconv.Atoi(v.(string))\n        return err == nil\n    }\n    return false\n}","tryCatchPattern":"html, err := frags.ToHTML(start, stop, ordered)\nif err != nil && strings.HasPrefix(err.Error(), \"startLevel:\") {\n    html, err = frags.ToHTML(2, stop, ordered) // sane default\n}","preventionTips":["Always pass integer literals/typed ints to ToHTML, never raw params.","Coerce front-matter values in the template before calling ToHTML.","Default startLevel to 2 to match DefaultConfig."],"tags":["table-of-contents","template","type-coercion","validation"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}