gohugoio/hugo · error

invalid type %T in related search

Error message

invalid type %T in related search

What it means

All related-content searches funnel through Pages.withInvertedIndex, which type-asserts the first page in the collection to RelatedDocsHandlerProvider to obtain the site's related-docs cache and inverted index. If that assertion fails — the collection's elements aren't real page objects backed by the site — the search cannot proceed and this error names the actual type found.

Source

Thrown at resources/page/pages_related.go:129

	}

	return p.search(ctx, opts)
}

func (p Pages) search(ctx context.Context, opts related.SearchOpts) (Pages, error) {
	return p.withInvertedIndex(ctx, func(idx *related.InvertedIndex) ([]related.Document, error) {
		return idx.Search(ctx, opts)
	})
}

func (p Pages) withInvertedIndex(ctx context.Context, search func(idx *related.InvertedIndex) ([]related.Document, error)) (Pages, error) {
	if len(p) == 0 {
		return nil, nil
	}

	d, ok := p[0].(RelatedDocsHandlerProvider)
	if !ok {
		return nil, fmt.Errorf("invalid type %T in related search", p[0])
	}

	cache := d.GetInternalRelatedDocsHandler()

	searchIndex, err := cache.getOrCreateIndex(ctx, p)
	if err != nil {
		return nil, err
	}

	result, err := search(searchIndex)
	if err != nil {
		return nil, err
	}

	if len(result) > 0 {
		mp := make(Pages, len(result))
		for i, match := range result {
			mp[i] = match.(Page)

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Run Related on a real page collection such as `site.RegularPages` or `.Pages` instead of a hand-assembled slice.
  2. If you filtered/assembled the collection yourself, make sure every element originates from site pages (e.g. use `where` on site.Pages rather than building a new slice from arbitrary values).
  3. In custom Go code or tests, use pages created by the Hugo site (hugolib.Test) so they implement the internal RelatedDocsHandlerProvider interface.

Example fix

<!-- before: hand-built collection of non-page values -->
{{ $coll := slice .Title .Section }}
{{ $related := $coll.Related . }}
<!-- after: search a real page collection -->
{{ $related := site.RegularPages.Related . }}
Defensive patterns

Strategy: type-guard

Type guard

func validRelatedKeyVal(v any) bool {
    switch v.(type) {
    case string, []string, time.Time:
        return true
    default:
        return false
    }
}

Prevention

When it happens

Trigger: Calling `.Related`, `.RelatedIndices`, or `.RelatedTo` on a Pages collection whose first element is not a full site-backed Page — e.g. a collection hand-built in a template from non-page values, or synthetic/wrapped page types (custom code, tests) that don't implement RelatedDocsHandlerProvider.

Common situations: Building a slice with `slice`/`append` in templates that mixes pages with other values, template constructs that yield page-like adapters rather than concrete pages, or (for Hugo module/tool developers) passing mock pages into the related API.

Related errors


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