{"id":"d1404123ac9a572b","repo":"gohugoio/hugo","slug":"resources-in-concat-must-be-of-the-same-media-type","errorCode":null,"errorMessage":"resources in Concat must be of the same Media Type, got %q and %q","messagePattern":"resources in Concat must be of the same Media Type, got %q and %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resources/resource_factories/bundler/bundler.go","lineNumber":92,"sourceCode":"\nfunc (r *multiReadSeekCloser) Close() error {\n\tfor _, s := range r.sources {\n\t\ts.Close()\n\t}\n\treturn nil\n}\n\n// Concat concatenates the list of Resource objects.\nfunc (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resource, error) {\n\ttargetPath = path.Clean(targetPath)\n\treturn c.rs.ResourceCache.GetOrCreate(targetPath, func() (resource.Resource, error) {\n\t\tvar resolvedm media.Type\n\n\t\t// The given set of resources must be of the same Media Type.\n\t\t// We may improve on that in the future, but then we need to know more.\n\t\tfor i, rr := range r {\n\t\t\tif i > 0 && rr.MediaType().Type != resolvedm.Type {\n\t\t\t\treturn nil, fmt.Errorf(\"resources in Concat must be of the same Media Type, got %q and %q\", rr.MediaType().Type, resolvedm.Type)\n\t\t\t}\n\t\t\tresolvedm = rr.MediaType()\n\t\t}\n\n\t\tidm := c.rs.Cfg.NewIdentityManager()\n\n\t\t// Re-create on structural changes.\n\t\tidm.AddIdentity(identity.StructuralChangeAdd, identity.StructuralChangeRemove)\n\n\t\t// Add the concatenated resources as dependencies to the composite resource\n\t\t// so that we can track changes to the individual resources.\n\t\tidm.AddIdentityForEach(identity.ForEeachIdentityProviderFunc(\n\t\t\tfunc(f func(identity.Identity) bool) bool {\n\t\t\t\tvar terminate bool\n\t\t\t\tfor _, rr := range r {\n\t\t\t\t\tidentity.WalkIdentitiesShallow(rr, func(depth int, id identity.Identity) bool {\n\t\t\t\t\t\tterminate = f(id)\n\t\t\t\t\t\treturn terminate","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/resources/resource_factories/bundler/bundler.go#L74-L110","documentation":"`resources.Concat` requires every resource in the slice to share the same media type (e.g. all `text/css` or all `text/javascript`), because the concatenated output resource must carry a single media type and Hugo cannot safely infer one from a mixed set. The check compares each resource's `MediaType().Type` against the first one and fails on the first mismatch.","triggerScenarios":"Calling `resources.Concat $target $slice` in a template where the slice mixes types — e.g. a `.css` file with a `.js` file, or a `.scss` resource that was not piped through `css.Sass` first (media type `text/x-scss` vs `text/css`), or minified vs source assets whose extensions map to different configured media types.","commonSituations":"Forgetting to run `toCSS`/`css.Sass` on SCSS resources before concatenating them with plain CSS; globbing too broadly (`resources.Match \"assets/**\"`) and picking up source maps or unrelated files; custom `mediaTypes` config mapping an extension to an unexpected type; template refactors that reorder pipes so Concat runs before the transform.","solutions":["Transform all resources to the target type before Concat, e.g. pipe SCSS through `css.Sass` (and JS through `js.Build`) first.","Tighten the glob so only one file type is matched, e.g. `resources.Match \"css/*.css\"` instead of a wildcard over mixed assets.","Print each resource's `.MediaType` in the template to find the odd one out, then fix its pipeline or the `mediaTypes` config."],"exampleFix":"{{/* before */}}\n{{ $css := slice (resources.Get \"main.scss\") (resources.Get \"extra.css\") | resources.Concat \"bundle.css\" }}\n{{/* after */}}\n{{ $main := resources.Get \"main.scss\" | css.Sass }}\n{{ $css := slice $main (resources.Get \"extra.css\") | resources.Concat \"bundle.css\" }}","handlingStrategy":"validation","validationCode":"{{/* pre-check media types before Concat in a template */}}\n{{ $files := slice (resources.Get \"a.js\") (resources.Get \"b.js\") }}\n{{ $mt := (index $files 0).MediaType.Type }}\n{{ range $files }}\n  {{ if ne .MediaType.Type $mt }}\n    {{ errorf \"Concat mix: %s vs %s (%s)\" $mt .MediaType.Type .Name }}\n  {{ end }}\n{{ end }}\n{{ $bundle := $files | resources.Concat \"bundle.js\" }}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only Concat resources with identical media types — group JS with JS, CSS with CSS","Use consistent file extensions; a .js vs .mjs mismatch can yield different media types","Run minify/toCSS transforms before Concat so all inputs converge to one type","Use resources.Match with a single-extension glob (e.g. \"js/*.js\") to build the concat slice"],"tags":["hugo","concat","media-type","asset-pipeline","templates"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}