gohugoio/hugo · error

content source: failed to convert %T to string: %s

Error message

content source: failed to convert %T to string: %s

What it means

`Source.ValueAsString` in page_frontmatter.go casts the front matter `content.value` to a string with `cast.ToStringE` and **panics** with this message when the value is not string-convertible. It's a panic, not a returned error, because callers such as `ValueAsOpenReadSeekCloser` treat the content as already-validated. The `%T` shows the actual Go type Hugo received from the decoded front matter.

Source

Thrown at resources/page/pagemeta/page_frontmatter.go:560

	Value any
}

func (s Source) IsZero() bool {
	return !hreflect.IsTruthful(s.Value)
}

func (s Source) IsResourceValue() bool {
	_, ok := s.Value.(resource.Resource)
	return ok
}

func (s Source) ValueAsString() string {
	if s.Value == nil {
		return ""
	}
	ss, err := cast.ToStringE(s.Value)
	if err != nil {
		panic(fmt.Errorf("content source: failed to convert %T to string: %s", s.Value, err))
	}
	return ss
}

func (s Source) ValueAsOpenReadSeekCloser() hugio.OpenReadSeekCloser {
	content := s.ValueAsString()
	return func() (hugio.ReadSeekCloser, error) {
		return hugio.NewReadSeekerNoOpCloserFromString(content), nil
	}
}

// FrontMatterOnlyValues holds values that can only be set via front matter.
type FrontMatterOnlyValues struct {
	ResourcesMeta []map[string]any
}

// FrontMatterHandler maps front matter into Page fields and .Params.
// Note that we currently have only extracted the date logic.

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Make `content.value` a scalar string — use a YAML block scalar (`value: |`) or TOML multi-line `"""` literal instead of a nested structure.
  2. Check the reported `%T` in the panic: `map[string]interface {}` or `[]interface {}` means the front matter parsed as a structure, so fix the indentation/quoting.
  3. If you meant to reference an existing resource, assign the resource object itself so `IsResourceValue` matches, rather than a wrapper map.
  4. Reproduce with a minimal page and `hugo --logLevel debug` to identify which page's front matter carries the bad value.

Example fix

# before — YAML parses as a map, panics
resources:
  - name: intro
    content:
      value:
        title: Hello

# after — scalar string
resources:
  - name: intro
    content:
      value: |
        title: Hello
Defensive patterns

Strategy: type-guard

Type guard

// ensure content source value is a string (or fmt.Stringer) before assigning
func isStringable(v any) bool {
    switch v.(type) {
    case string, fmt.Stringer, []byte:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Setting a page resource's `content.value` to a map, slice/array, or arbitrary struct instead of a scalar — e.g. a YAML block that parses as a nested mapping or a list — and then having Hugo read that resource's content (via `.Content`, `.ReadSeekCloser`, or publishing it).

Common situations: YAML indentation errors that turn an intended multi-line string into a nested map or sequence; TOML arrays used for multi-line content; passing a `resource.Resource` or a template-produced dict into `content.value` when a literal string was intended (note `IsResourceValue` handles genuine Resource values, so this panic means an unsupported third type).

Related errors


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