gohugoio/hugo · error

failed to create resource for path

Error message

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

What it means

Returned by Spec.NewResourceWrapperFromResourceConfig when the ResourceConfig.Content.Value is not a resource.Resource. The function type-switches on content.Value expecting an already-constructed resource.Resource; any other concrete type falls into the default branch and is reported via %T. This is an internal contract violation in how a ResourceConfig is assembled, not normally user-facing.

Solutions

  1. If you are calling NewResourceWrapperFromResourceConfig directly, ensure rc.Content.Value is a resource.Resource (e.g. produced by Spec.NewResource or resources.NewPointerResource) before calling it.
  2. If this surfaces from a normal build, file a Hugo issue with the stack trace and the offending rc.PathInfo; it indicates an internal contract break rather than a config mistake.
  3. Upgrade Hugo to the latest release; this is frequently fixed by an upstream patch since it is an internal invariant.

Example fix

// before
rc := &pagemeta.ResourceConfig{PathInfo: pi}
rc.Content.Value = []byte(rawBytes) // wrong type

// after
res, _ := spec.NewResource(descriptor) // returns resource.Resource
rc.Content.Value = res
Defensive patterns

Strategy: type-guard

Validate before calling

// Before constructing a ResourceConfig, assert the value type.
func isResource(v any) bool {
    _, ok := v.(resource.Resource)
    return ok
}

if !isResource(rc.Content.Value) {
    return fmt.Errorf("Content.Value must be resource.Resource, got %T", rc.Content.Value)
}

Type guard

// Go type guard for resource.Resource.
func asResource(v any) (resource.Resource, bool) {
    r, ok := v.(resource.Resource)
    return r, ok
}

Prevention

When it happens

Trigger: Internal code path constructs a *pagemeta.ResourceConfig whose Content.Value is set to a plain string, []byte, os.File, or a custom type instead of a resource.Resource, then calls NewResourceWrapperFromResourceConfig. Typically reached via page resource parsing or the resources.FromString pipeline when a downstream caller mis-wraps the content.

Common situations: A Hugo internal refactor or third-party module/hook that builds ResourceConfig objects and forgets to wrap raw content in a resource.ResourceLink / resource clone. End users almost never see this unless they are calling the resources package directly or hit a regression.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/c4feffaa6cf1cd24. Report an issue: GitHub.

Appendix: 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 52c9bd7908)