gohugoio/hugo · error

failed to build JS batch

Error message

failed to build JS batch %q: %w

What it means

The batcher.Build entry point (batch.go:337-345) fetches-or-creates the batch via a dynacache store, calling the internal build(). If build() returns an error, Build wraps it with the batch id (%q) via %w. This is the top-level failure for a JS batch and aggregates all downstream causes (template execution, esbuild bundling, resource creation, group builds).

Solutions

  1. Unwrap the %w to find the concrete cause (esbuild error message, template error, etc.).
  2. If it is an esbuild error, open the offending JS/TS entry and fix the syntax/import.
  3. If it is 'only esm format is currently supported', set options.Format to "esm" (or leave empty).
  4. Rebuild after fixing; clear the dynacache if stale artifacts persist.

Example fix

// before: options.Format = "iife"
// error: failed to build JS batch "main": only esm format is currently supported

// after
options.Format = "esm"
Defensive patterns

Strategy: try-catch

Validate before calling

func safeBuild(ctx context.Context, b *batcher) (js.BatchPackage, error) {
    pkg, err := b.Build(ctx)
    if err != nil {
        return nil, fmt.Errorf("batch build failed: %w", err)
    }
    return pkg, nil
}

Try / catch

pkg, err := batcher.Build(ctx)
if err != nil {
    var fe *fmt.Errorf // unwrap to inspect underlying esbuild/template cause
    log.Printf("batch build error: %+v", err)
    return err
}

Prevention

When it happens

Trigger: Any failure inside build(): a template error in the batch runner template, esbuild failing to bundle (syntax error, missing import, unsupported format), resource creation errors, or the 'only esm format' check. The id names which batch failed.

Common situations: A JS import typo or missing dependency surfaced through esbuild; a batch group template error; passing a non-esm format; a resource FromString failure due to invalid generated JS.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/11be01f57c408dc6. Report an issue: GitHub.

Appendix: source

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

	client            *BatcherClient
	dependencyManager identity.Manager

	configOptions optionsGetSetter[scriptID, configOptions]

	// The last successfully built package.
	// If this is non-nil and not stale, we can reuse it (e.g. on server rebuilds)
	prevBuild *Package
}

// Build builds the batch if not already built or if it's stale.
func (b *batcher) Build(ctx context.Context) (js.BatchPackage, error) {
	key := dynacache.CleanKey(b.id + ".js")
	p, err := b.client.bundlesStore.GetOrCreate(key, func() (js.BatchPackage, error) {
		return b.build(ctx)
	})
	if err != nil {
		return nil, fmt.Errorf("failed to build JS batch %q: %w", b.id, err)
	}
	return p, nil
}

func (b *batcher) Config(ctx context.Context) js.OptionsSetter {
	return b.configOptions.Get(b.buildCount)
}

func (b *batcher) Group(ctx context.Context, id string) js.BatcherGroup {
	if err := ValidateBatchID(id, false); err != nil {
		panic(err)
	}

	b.mu.Lock()
	defer b.mu.Unlock()

	group, found := b.scriptGroups[id]
	if !found {

View on GitHub (pinned to 52c9bd7908)