gohugoio/hugo · error

resources in Concat must be of the same Media Type, got

Error message

resources in Concat must be of the same Media Type, got %q and %q

What it means

Returned by `bundler.Client.Concat` (bundler.go:83-95) when two resources in the input list report different `MediaType().Type`. Concat physically joins bytes; mixing media types would corrupt the output (e.g. concatenating CSS into JS). The check at bundler.go:90-93 compares each resource's media type against the first and fails on the first mismatch.

Solutions

  1. Filter the input list to a single media type before calling Concat (e.g. `resources.Match "js/**"`).
  2. Verify each resource's `.MediaType.Type` with a debug print before concat.
  3. Align `mediaTypes` config so equivalent assets share a type.
  4. Split the concat into one call per media type.

Example fix

// template -- before
{{ $bundle := resources.Concat "bundle.js" (resources.Match "*") }}

// after
{{ $bundle := resources.Concat "bundle.js" (resources.Match "js/**") }}
Defensive patterns

Strategy: validation

Validate before calling

// In template, verify uniform media type before concat.
{{ $types := slice }}
{{ range $r := $input }}{{ $types = $types | append $r.MediaType.Type }}{{ end }}
{{ if lt (len (uniq $types)) 2 }}{{ $bundle := resources.Concat "out" $input }}{{ end }}

Try / catch

// Guard concat at the Go call site.
first := list[0].MediaType().Type
for _, r := range list[1:] {
	if r.MediaType().Type != first {
		return nil, fmt.Errorf("media type mismatch: %s vs %s", r.MediaType().Type, first)
	}
}
return bundler.Concat(...)

Prevention

When it happens

Trigger: Calling `resources.Concat` (template) / `bundler.Client.Concat` with a list containing, say, a CSS resource and a JS resource, or a JSON and a text resource, or two resources whose media types were configured/assigned differently.

Common situations: Bundle pipeline that pulls from `.Resources.Match "*"` and accidentally includes a non-JS asset (README, sourcemap, image); media type misconfigured in `mediaTypes` config so two logically same files report different suffixes; partial templates re-used across themes with different default media types.

Related errors


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

Appendix: source

Thrown at resources/resource_factories/bundler/bundler.go:92

func (r *multiReadSeekCloser) Close() error {
	for _, s := range r.sources {
		s.Close()
	}
	return nil
}

// Concat concatenates the list of Resource objects.
func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resource, error) {
	targetPath = path.Clean(targetPath)
	return c.rs.ResourceCache.GetOrCreate(targetPath, func() (resource.Resource, error) {
		var resolvedm media.Type

		// The given set of resources must be of the same Media Type.
		// We may improve on that in the future, but then we need to know more.
		for i, rr := range r {
			if i > 0 && rr.MediaType().Type != resolvedm.Type {
				return nil, fmt.Errorf("resources in Concat must be of the same Media Type, got %q and %q", rr.MediaType().Type, resolvedm.Type)
			}
			resolvedm = rr.MediaType()
		}

		idm := c.rs.Cfg.NewIdentityManager()

		// Re-create on structural changes.
		idm.AddIdentity(identity.StructuralChangeAdd, identity.StructuralChangeRemove)

		// Add the concatenated resources as dependencies to the composite resource
		// so that we can track changes to the individual resources.
		idm.AddIdentityForEach(identity.ForEeachIdentityProviderFunc(
			func(f func(identity.Identity) bool) bool {
				var terminate bool
				for _, rr := range r {
					identity.WalkIdentitiesShallow(rr, func(depth int, id identity.Identity) bool {
						terminate = f(id)
						return terminate

View on GitHub (pinned to 52c9bd7908)