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

  1. Use a loader name esbuild supports: replace "url" with "dataurl", "raw" with "text", etc.
  2. Check spelling against esbuild's content-types docs (no hyphens: "dataurl", not "data-url").
  3. 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

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


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/3832fc16e4f4308e.json. Report an issue: GitHub ↗.