{"id":"72362fe573e8d63e","repo":"gohugoio/hugo","slug":"failed-to-convert-content-to-string-w","errorCode":null,"errorMessage":"failed to convert content to string: %w","messagePattern":"failed to convert content to string: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/strings/strings.go","lineNumber":57,"sourceCode":"\treturn &Namespace{\n\t\tdeps:          d,\n\t\treplacerCache: hmaps.NewCacheWithOptions[string, *strings.Replacer](hmaps.CacheOptions{Size: 100}),\n\t}\n}\n\n// Namespace provides template functions for the \"strings\" namespace.\n// Most functions mimic the Go stdlib, but the order of the parameters may be\n// different to ease their use in the Go template system.\ntype Namespace struct {\n\tdeps          *deps.Deps\n\treplacerCache *hmaps.Cache[string, *strings.Replacer]\n}\n\n// CountRunes returns the number of runes in s, excluding whitespace.\nfunc (ns *Namespace) CountRunes(s any) (int, error) {\n\tss, err := cast.ToStringE(s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to convert content to string: %w\", err)\n\t}\n\n\tcounter := 0\n\tfor _, r := range tpl.StripHTML(ss) {\n\t\tif !helpers.IsWhitespace(r) {\n\t\t\tcounter++\n\t\t}\n\t}\n\n\treturn counter, nil\n}\n\n// RuneCount returns the number of runes in s.\nfunc (ns *Namespace) RuneCount(s any) (int, error) {\n\tss, err := cast.ToStringE(s)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"failed to convert content to string: %w\", err)\n\t}","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/tpl/strings/strings.go#L39-L75","documentation":"Hugo's `strings.CountRunes` (and the identically-messaged `strings.RuneCount` / `strings.CountWords`) first converts its argument to a string with `cast.ToStringE`. That cast succeeds for strings, numbers, template.HTML, and fmt.Stringer types, but fails for maps, slices, Page objects, and other composites — producing this wrapped error with the underlying cast failure as `%w`.","triggerScenarios":"Calling `{{ strings.CountRunes X }}`, `{{ strings.RuneCount X }}`, or `{{ countwords X }}` where X is not string-convertible: passing `.Pages`, a dict/slice, or a raw Page object instead of `.Content`/`.Plain`.","commonSituations":"Passing `.` (a Page) instead of `.Content` inside a template; piping the result of `where`/`slice` into a counting function; passing `.Params.tags` (a slice) where a single string was intended; reading-time or word-count partials applied to the wrong context variable.","solutions":["Pass a string field explicitly: `{{ strings.CountRunes .Content }}` or `.Plain`, not the page object itself.","If the value is a slice you meant to join, use `{{ strings.CountRunes (delimit . \" \") }}`.","Inspect the wrapped error text — it names the actual Go type that failed to convert — and fix that variable at its source."],"exampleFix":"<!-- before -->\n{{ strings.CountRunes . }}\n<!-- after -->\n{{ strings.CountRunes .Content }}","handlingStrategy":"try-catch","validationCode":"{{ if or (eq (printf \"%T\" $v) \"string\") ($v | printf \"%v\") }}{{ findRE $pattern (string $v) }}{{ end }}","typeGuard":"func isStringable(v any) bool {\n    _, err := cast.ToStringE(v)\n    return err == nil\n}","tryCatchPattern":"s, err := cast.ToStringE(content)\nif err != nil {\n    return nil, fmt.Errorf(\"content not stringable: %w\", err)\n}","preventionTips":["Pass strings or types implementing fmt.Stringer to string functions, not maps/slices","Use `string $v` or `printf \"%v\" $v` to coerce explicitly before regex/string functions","Don't pass whole page objects — pass `.Content`, `.Plain`, or `.Title`"],"tags":["hugo","go-templates","strings","type-conversion"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}