gohugoio/hugo · error

text is already rendered, repeating it may cause infinite re

Error message

text is already rendered, repeating it may cause infinite recursion

What it means

Raised in RenderString (page__content.go) when the value passed in is already of type hstring.HTML — i.e. output that Hugo's own render pipeline produced (such as the result of a render hook's .Text or a previous RenderString). Re-rendering already-rendered HTML is at best a no-op and at worst causes infinite recursion (a render hook calling RenderString on its own output), so Hugo refuses.

Source

Thrown at hugolib/page__content.go:912

		m, ok := args[0].(map[string]any)
		if !ok {
			return "", errors.New("first argument must be a map")
		}

		if err := mapstructure.WeakDecode(m, &opts); err != nil {
			return "", fmt.Errorf("failed to decode options: %w", err)
		}
		if opts.Markup != "" {
			opts.Markup = markup.ResolveMarkup(opts.Markup)
		}
	}

	contentToRenderv := args[sidx]

	if _, ok := contentToRenderv.(hstring.HTML); ok {
		// This content is already rendered, this is potentially
		// a infinite recursion.
		return "", errors.New("text is already rendered, repeating it may cause infinite recursion")
	}

	var err error
	contentToRender, err = cast.ToStringE(contentToRenderv)
	if err != nil {
		return "", err
	}

	if err = pco.initRenderHooks(); err != nil {
		return "", err
	}

	conv := pco.po.p.getContentConverter()

	if opts.Markup != "" && opts.Markup != pco.po.p.m.pageConfigSource.ContentMediaType.SubType {
		var err error
		conv, err = pco.po.p.m.newContentConverter(pco.po.p, opts.Markup)
		if err != nil {

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Don't re-render: use the already-rendered value directly, e.g. `{{ .Text }}` in the render hook.
  2. If you genuinely need to re-process, work from the raw source instead — e.g. `.PlainText` in a render hook, or the original front-matter string — and pass that to RenderString.
  3. Convert explicitly only when you understand the recursion risk: `{{ .Page.RenderString (string $v) }}` forces plain-string handling.

Example fix

{{/* layouts/_markup/render-link.html before */}}
<a href="{{ .Destination }}">{{ .Page.RenderString .Text }}</a>
{{/* after */}}
<a href="{{ .Destination }}">{{ .Text }}</a>
Defensive patterns

Strategy: validation

Validate before calling

{{/* never feed already-rendered content back into RenderString */}}
{{ $raw := .RawContent }}
{{ $out := .RenderString $raw }} {{/* OK: raw, not .Content */}}

Prevention

When it happens

Trigger: A markdown render hook (e.g. layouts/_markup/render-link.html) passing `.Text` (already-rendered HTML) into `.Page.RenderString`; templates piping the result of one RenderString/markdownify call into another RenderString.

Common situations: Render-hook authors trying to post-process link/heading text with RenderString; recursive shortcode/hook chains introduced when porting themes to newer Hugo versions where .Text became typed HTML; double-processing front-matter fields once through markdownify and again through RenderString.

Related errors


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