gohugoio/hugo · error
invalid loader: %q
Error message
invalid loader: %q
What it means
The `loaders` option of js.Build maps file extensions to esbuild loaders (e.g. ".png" → "dataurl"). Hugo translates each configured loader name through its nameLoader table into an esbuild api.Loader; a name not in that table aborts option decoding, since esbuild would otherwise misinterpret the files.
Source
Thrown at internal/js/esbuild/options.go:315
target = api.ESNext
}
var loaders map[string]api.Loader
if opts.IsCSS {
loaders = make(map[string]api.Loader)
// Add default CSS file loaders.
// May be overridden by opts.Loaders.
maps.Copy(loaders, extensionToLoaderMapCSS)
}
if opts.Loaders != nil {
if loaders == nil {
loaders = make(map[string]api.Loader)
}
for k, v := range opts.Loaders {
loader, found := nameLoader[v]
if !found {
err = fmt.Errorf("invalid loader: %q", v)
return
}
loaders[k] = loader
}
}
mediaType := opts.MediaType
if mediaType.IsZero() {
mediaType = media.Builtin.JavascriptType
}
var loader api.Loader
switch mediaType.SubType {
case media.Builtin.JavascriptType.SubType:
loader = api.LoaderJS
case media.Builtin.TypeScriptType.SubType:
loader = api.LoaderTS
case media.Builtin.TSXType.SubType:View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Use a loader name esbuild supports: replace "url" with "dataurl", "raw" with "text", etc.
- Check spelling against esbuild's content-types docs (no hyphens: "dataurl", not "data-url").
- If the loader is newer than your Hugo's bundled esbuild, upgrade Hugo.
Example fix
{{/* before */}}
{{ $opts := dict "loaders" (dict ".svg" "url") }}
{{/* after */}}
{{ $opts := dict "loaders" (dict ".svg" "dataurl") }} Defensive patterns
Strategy: validation
Validate before calling
validLoaders := map[string]bool{"none":true,"base64":true,"binary":true,"copy":true,"css":true,"dataurl":true,"empty":true,"file":true,"js":true,"json":true,"jsx":true,"text":true,"ts":true,"tsx":true}
for ext, l := range opts.Loaders {
if !validLoaders[l] {
return fmt.Errorf("invalid loader %q for %s", l, ext)
}
} Prevention
- Only use esbuild loader names (js, jsx, ts, tsx, json, css, text, base64, dataurl, file, ...)
- Loader names are lowercase — "TSX" is invalid
- Validate any user-supplied loader map in templates before calling js.Build
When it happens
Trigger: `js.Build` with a `loaders` dict whose value isn't a recognized loader name, e.g. `dict "loaders" (dict ".svg" "url")` — "url" isn't in Hugo's loader map. Valid names include js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary, copy, empty.
Common situations: Using webpack loader names ("url", "raw") instead of esbuild's ("dataurl", "text"); typos like "data-url"; expecting a loader added in a newer esbuild than the one vendored in the installed Hugo version.
Related errors
- inject: absolute paths not supported, must be relative to /a
- invalid engine version: %q
- unsupported target: %v
- unsupported script output format: %q
- inject: file %q not found
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/3832fc16e4f4308e.json.
Report an issue: GitHub ↗.