gohugoio/hugo · error

id must not contain backslashes

Error message

id must not contain backslashes

What it means

Batch IDs are used to build cache keys and URL/output paths, which must use forward slashes on every OS. `ValidateBatchID` rejects any ID containing a backslash so Windows-style path fragments can't leak into keys and produce platform-dependent output. (Non-top-level IDs additionally reject forward slashes.)

Source

Thrown at internal/js/esbuild/batch.go:158

func (o *optsHolder[C]) SetOptions(m map[string]any) string {
	o.optsSetCounter++
	o.optsPrev = o.optsCurr
	o.optsCurr = m
	o.compiledPrev = o.compiled
	o.compiled, o.compileErr = o.compiled.compileOptions(m, o.defaults)
	o.checkCompileErr()
	return ""
}

// ValidateBatchID validates the given ID according to some very
func ValidateBatchID(id string, isTopLevel bool) error {
	if id == "" {
		return fmt.Errorf("id must be set")
	}
	// No Windows slashes.
	if strings.Contains(id, "\\") {
		return fmt.Errorf("id must not contain backslashes")
	}

	// Allow forward slashes in top level IDs only.
	if !isTopLevel && strings.Contains(id, "/") {
		return fmt.Errorf("id must not contain forward slashes")
	}

	return nil
}

func newIsBuiltOrTouched() isBuiltOrTouched {
	return isBuiltOrTouched{
		built:   make(buildIDs),
		touched: make(buildIDs),
	}
}

func newOpts[K any, C optionsCompiler[C]](key K, optionsID string, defaults defaultOptionValues) *opts[K, C] {

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Use forward slashes (top-level IDs only) or plain segment names with no path separators.
  2. If deriving from a file path, normalize first, e.g. `replace $path "\\" "/"` or use path.Base/filepath-agnostic helpers.
  3. Prefer simple stable identifiers ("main", "search") over path-derived IDs.

Example fix

{{/* before */}}
{{ $batch := js.Batch "js\\main" }}
{{/* after */}}
{{ $batch := js.Batch "js/main" }}
Defensive patterns

Strategy: validation

Validate before calling

if strings.ContainsRune(id, '\\') {
    id = strings.ReplaceAll(id, "\\", "/")
}

Type guard

func validBatchID(id string) bool {
    return id != "" && !strings.ContainsRune(id, '\\')
}

Prevention

When it happens

Trigger: Passing an ID like `js.Batch "js\\main"` or a group/script ID built from a Windows file path (`.File.Path` on Windows yields backslashes) containing `\`.

Common situations: Deriving IDs from filesystem paths on Windows; escaping mistakes in templates producing literal backslashes; copying paths from Windows Explorer into config.

Related errors


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