gohugoio/hugo · error

failed to create resource for path %q, expected a resource.R

Error message

failed to create resource for path %q, expected a resource.Resource, got %T

What it means

`Spec.NewResourceWrapperFromResourceConfig` in resources/resource_spec.go wraps an already-existing resource declared in front matter. It type-switches on `rc.Content.Value` and only accepts a `resource.Resource`; anything else means the front matter asked Hugo to wrap something that isn't a resource, and it reports the path plus the actual Go type. This path is distinct from creating a resource from literal content — that goes through `NewResource`.

Source

Thrown at resources/resource_spec.go:169

	FileCaches    filecache.Caches

	// Assets used after the build is done.
	// This is shared between all sites.
	*PostBuildAssets
}

type PostBuildAssets struct {
	PostProcessResources *maphelpers.ConcurrentMap[string, postpub.PostPublishedResource]
	JSConfigBuilder      *jsconfig.Builder
}

func (r *Spec) NewResourceWrapperFromResourceConfig(rc *pagemeta.ResourceConfig) (resource.Resource, error) {
	content := rc.Content
	switch r := content.Value.(type) {
	case resource.Resource:
		return cloneWithMetadataFromResourceConfigIfNeeded(rc, r), nil
	default:
		return nil, fmt.Errorf("failed to create resource for path %q, expected a resource.Resource, got %T", rc.PathInfo.Path(), content.Value)
	}
}

// NewResource creates a new Resource from the given ResourceSourceDescriptor.
func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, error) {
	if err := rd.init(r); err != nil {
		return nil, err
	}

	dir, name := path.Split(rd.TargetPath)
	dir = paths.ToSlashPreserveLeading(dir)
	if dir == "/" {
		dir = ""
	}
	rp := internal.ResourcePaths{
		File:            name,
		Dir:             dir,
		BaseDirTarget:   rd.BasePathTargetPath,

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Resolve the value to an actual resource first (e.g. `resources.Get` or `.Resources.GetMatch`) and assign that object to `content.value`.
  2. If you intended literal inline content rather than wrapping an existing resource, use the inline-content form with `content.mediaType` + string `value` so Hugo takes the create path instead of the wrap path.
  3. Check the `%T` in the message — `string` almost always means a path was passed where the resolved Resource was expected.
  4. Guard against nil: a failed `resources.Get` returns nil, which will also land here, so check the lookup succeeded before assigning.

Example fix

{{/* before */}}
{{ $rc := dict "path" "images/hero.jpg" "content" (dict "value" "images/hero.jpg") }}

{{/* after */}}
{{ $r := resources.Get "images/hero.jpg" }}
{{ with $r }}
  {{ $rc := dict "path" "images/hero.jpg" "content" (dict "value" .) }}
{{ end }}
Defensive patterns

Strategy: type-guard

Type guard

// when implementing custom resource factories, guarantee a resource.Resource
func asResource(v any) (resource.Resource, bool) {
    r, ok := v.(resource.Resource)
    return r, ok
}

Prevention

When it happens

Trigger: A page-resource entry in front matter whose `content.value` is a plain string, map, or number where a resource object was required by the code path (resource wrapping rather than resource creation); or template/module code building a `pagemeta.ResourceConfig` and passing a non-Resource value into `Content.Value` before calling `NewResourceWrapperFromResourceConfig`.

Common situations: Programmatically constructing page resources from a data file or template and forgetting to resolve the value through `resources.Get`/`.Resources.Get` first, so a path string is passed instead of the Resource; mixing the two front matter styles (inline literal content vs. referencing an existing resource) in one entry.

Related errors


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