gohugoio/hugo · error

text must be a string

Error message

text must be a string

What it means

Hugo's `truncate` template function casts its text argument to a string with `cast.ToStringE` after parsing the length and optional ellipsis. If that cast fails — i.e. the value passed as the text is not string-convertible (a map, slice, Page object, etc.) — Truncate returns this error. It fires only after the length argument was successfully parsed as an int, so it specifically means the second (or third) argument is the wrong type.

Source

Thrown at tpl/strings/truncate.go:73

	case 1:
		textParam = options[0]
		ellipsis = " …"
	case 2:
		textParam = options[1]
		ellipsis, err = cast.ToStringE(options[0])
		if err != nil {
			return "", errors.New("ellipsis must be a string")
		}
		if _, ok := options[0].(template.HTML); !ok {
			ellipsis = html.EscapeString(ellipsis)
		}
	default:
		return "", errors.New("too many arguments passed to truncate")
	}

	text, err := cast.ToStringE(textParam)
	if err != nil {
		return "", errors.New("text must be a string")
	}

	_, isHTML := textParam.(template.HTML)

	if utf8.RuneCountInString(text) <= length {
		if isHTML {
			return template.HTML(text), nil
		}
		return template.HTML(html.EscapeString(text)), nil
	}

	tags := []htmlTag{}
	var lastWordIndex, lastNonSpace, currentLen, endTextPos, nextTag int

	for i, r := range text {
		if i < nextTag {
			continue
		}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Ensure the text argument is a string: pass `.Summary`, `.Plain`, or a string param, not an object or slice.
  2. Check argument order — it's `truncate LENGTH [ELLIPSIS] TEXT`; the text comes last.
  3. Convert explicitly before truncating, e.g. `{{ truncate 50 (string .Params.title) }}` or `delimit` a slice into a string first.

Example fix

<!-- before -->
{{ truncate 50 .Params.tags }}
<!-- after -->
{{ truncate 50 (delimit .Params.tags ", ") }}
Defensive patterns

Strategy: type-guard

Validate before calling

{{ $v := .Params.summary }}
{{ if eq (printf "%T" $v) "string" }}{{ truncate 50 $v }}{{ end }}

Type guard

{{/* narrow to string before truncate */}}
{{ $s := "" }}
{{ with $v }}{{ $s = printf "%v" . }}{{ end }}
{{ truncate 50 $s }}

Prevention

When it happens

Trigger: Calling `{{ truncate 50 .Params.items }}` where the value is a slice/map; passing arguments in the wrong order like `{{ truncate .Summary 50 }}` (string first, so length cast fails earlier, but e.g. `{{ truncate 50 "..." .Pages }}` hits this); piping a non-string value: `{{ .Pages | truncate 10 }}`.

Common situations: Front-matter params that are lists or maps instead of strings; piping a Page or Resource object instead of `.Content`/`.Summary`; refactoring a template and swapping argument order; a param that is sometimes a string and sometimes a list across content files.

Related errors


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