gohugoio/hugo · error
failed to build JS batch: %w
Error message
failed to build JS batch: %w
What it means
During the per-group build loop inside build(), buildBatchGroup (batch.go:506-509) executes the runner template and creates a resource via FromString; if either step fails the error is wrapped as 'failed to build JS batch'. Unlike 397 (which carries the batch id), this variant wraps the group-level failure without the id and is one layer deeper.
Source
Thrown at internal/js/esbuild/batch.go:508
return k.scriptID == vv.Key()
}
for _, vvv := range instances.Filter(predicate) {
bt.Instances = append(bt.Instances, scriptInstanceBatchTemplateContext{opts: vvv})
}
t.Scripts = append(t.Scripts, bt)
}
for _, vv := range g.runnersOptions.ByKey() {
runnerKeyPath := keyPath + "_" + vv.Key().String()
runnerImpPath := paths.AddLeadingSlash(runnerKeyPath + "_runner" + vv.Compiled().Resource.MediaType().FirstSuffix.FullSuffix)
t.Runners = append(t.Runners, scriptRunnerTemplateContext{opts: vv, Import: runnerImpPath})
addResource(g.id, runnerImpPath, vv.Compiled().Resource, false)
}
r, s, err := b.client.buildBatchGroup(ctx, t)
if err != nil {
return nil, fmt.Errorf("failed to build JS batch: %w", err)
}
state.importerImportContext.Set(s, importContext{
name: s,
resourceGetter: nil,
dm: g.dependencyManager,
})
addResource(g.id, s, r, true)
}
mediaTypes := b.client.d.ResourceSpec.MediaTypes()
externalOptions := b.configOptions.Compiled().Options
if externalOptions.Format == "" {
externalOptions.Format = "esm"
}
if externalOptions.Format != "esm" {View on GitHub (pinned to 52c9bd7908)
Solutions
- Inspect the wrapped error to distinguish template-execution failure from resource-creation failure.
- For template errors, check the runner template against the current batchGroupTemplateContext fields.
- For FromString errors, verify each group has valid scripts and a unique keyPath.
- Reproduce with a single group to isolate the failing one.
Example fix
// before: runner template references {{ .MissingField }}
// after: use a field that exists on batchGroupTemplateContext (e.g. {{ .ID }}) Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure each group has at least one script and a unique keyPath before build.
func validateGroup(g *scriptGroup) error {
if g.id == "" { return fmt.Errorf("group id empty") }
if len(g.scriptsOptions.ByKey()) == 0 && len(g.runnersOptions.ByKey()) == 0 {
return fmt.Errorf("group %q has no scripts/runners", g.id)
}
return nil
} Try / catch
// Wrap per-group build to surface a clear id in logs.
for _, g := range groups {
if err := buildGroup(ctx, g); err != nil {
return fmt.Errorf("group %q: %w", g.id, err)
}
} Prevention
- Keep runner templates in sync with batchGroupTemplateContext fields after upgrades.
- Ensure unique group ids to avoid virtual path collisions.
- Build groups in isolation while debugging to localize failures.
When it happens
Trigger: The runner template (batchGroupTemplateContext) fails to execute (bad template syntax, missing field), or createClient.FromString rejects the generated JS (empty/invalid content, path conflict). Occurs once per script group in the batch.
Common situations: A runner template referencing a field removed from batchGroupTemplateContext after an upgrade; generated JS that is empty due to no scripts in the group; a virtual path collision from duplicate group ids.
Related errors
- failed to build JS batch %q: %w
- id must be set
- id must not contain backslashes
- id must not contain forward slashes
- only esm format is currently supported
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/e8316a01dc7c150f.
Report an issue: GitHub.