gohugoio/hugo · error
must provide one or more Resource objects to concat
Error message
must provide one or more Resource objects to concat
What it means
After type-checking, `resources.Concat` verifies the Resources slice is non-empty; concatenating zero resources would produce nothing, so Hugo fails fast instead of emitting an empty output file. This is Hugo's fail-fast guard against silently publishing an empty bundle.
Source
Thrown at tpl/resources/resources.go:219
func (ns *Namespace) Concat(targetPathIn any, r any) (resource.Resource, error) {
targetPath, err := cast.ToStringE(targetPathIn)
if err != nil {
return nil, err
}
var rr resource.Resources
switch v := r.(type) {
case resource.Resources:
rr = v
case resource.ResourcesConverter:
rr = v.ToResources()
default:
return nil, fmt.Errorf("expected slice of Resource objects, received %T instead", r)
}
if len(rr) == 0 {
return nil, errors.New("must provide one or more Resource objects to concat")
}
return ns.bundlerClient.Concat(targetPath, rr)
}
// FromString creates a Resource from a string published to the relative target path.
func (ns *Namespace) FromString(targetPathIn, contentIn any) (resource.Resource, error) {
targetPath, err := cast.ToStringE(targetPathIn)
if err != nil {
return nil, err
}
content, err := cast.ToStringE(contentIn)
if err != nil {
return nil, err
}
return ns.createClient.FromString(targetPath, content)
}View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Confirm the files live under /assets (or a configured assetDir), not /static.
- Fix the glob: `*` doesn't match `/`; use `js/*.js` or `**.js`.
- Guard the concat: `{{ with resources.Match "js/*.js" }}{{ $b := resources.Concat "js/bundle.js" . }}{{ end }}`.
Example fix
<!-- before -->
{{ $b := resources.Concat "bundle.js" (resources.Match "*.js") }}
<!-- after -->
{{ with resources.Match "js/**.js" }}
{{ $b := resources.Concat "bundle.js" . }}
{{ end }} Defensive patterns
Strategy: validation
Validate before calling
{{ $items := slice }}{{ with resources.Get "a.css" }}{{ $items = $items | append . }}{{ end }}{{ if $items }}{{ $bundle := $items | resources.Concat "bundle.css" }}{{ end }} Type guard
{{ $nonEmpty := gt (len $items) 0 }} Try / catch
{{ with try (resources.Concat "bundle.css" $items) }}{{ with .Err }}{{ warnf "nothing to concat: %s" . }}{{ end }}{{ end }} Prevention
- Check the slice is non-empty before calling Concat — conditional appends can leave it empty
- Fail fast with errorf if expected assets are missing rather than concatenating an empty set
When it happens
Trigger: `resources.Concat $target (resources.Match "js/*.js")` when the glob matches no files in /assets; building the slice conditionally and every condition being false; filtering a Resources slice down to empty with `where`.
Common situations: Assets placed in /static instead of /assets (Match only searches the assets filesystem); a glob pattern that doesn't cross directories (`*.js` doesn't match `js/app.js` — use `js/*.js` or `**.js`); theme expecting asset files the site doesn't provide; environment-conditional bundles.
Related errors
- expected slice of Resource objects, received %T instead
- resource %T does not implement resource.ReadSeekerCloserReso
- failed to copy resources to vendor dir: %w
- no Key set in Resource
- resources.PostProcess cannot be used in a deferred template
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/ff078de436b5f988.json.
Report an issue: GitHub ↗.