evanw/esbuild · critical

Invalid tree shaking

Error message

Invalid tree shaking

What it means

validateTreeShaking (pkg/api/api_impl.go:258) panics when TreeShaking is not TreeShakingDefault(0), TreeShakingFalse(1), or TreeShakingTrue(2). For TreeShakingDefault the function computes a value from `bundle` and `format` (true when bundling or IIFE; otherwise false to be safe with later concatenation); any other numeric value is undefined and aborts. Reached from validateBuildOptions.

Solutions

  1. Prefer api.TreeShakingDefault (lets esbuild pick based on bundle/format); or set api.TreeShakingTrue / api.TreeShakingFalse explicitly.
  2. Bounds-check decoded integers against 0..2 before casting.
  3. Serialize the option as a string and map to the constant at load.
  4. Realign wrapper and core esbuild versions.

Example fix

// before
opts := api.BuildOptions{TreeShaking: api.TreeShaking(7), Bundle: true}

// after
opts := api.BuildOptions{TreeShaking: api.TreeShakingDefault, Bundle: true}
Defensive patterns

Strategy: validation

Validate before calling

func checkTreeShaking(t api.TreeShaking) error {
    switch t {
    case api.TreeShakingDefault, api.TreeShakingFalse, api.TreeShakingTrue:
        return nil
    }
    return fmt.Errorf("invalid tree shaking %d (want 0..2)", uint8(t))
}

Type guard

func isValidTreeShaking(t api.TreeShaking) bool {
    switch t {
    case api.TreeShakingDefault, api.TreeShakingFalse, api.TreeShakingTrue:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Passing a numeric TreeShaking from a cast, JSON-decoded struct, or inter-language bridge with mismatched ordinals. Fires during options construction, before scanning.

Common situations: Persisted configs that store tree-shaking as an int, version drift between a wrapper and core, or a typo such as `TreeShaking: api.TreeShakingBoth`.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/12a4beff3f8821d0. Report an issue: GitHub.

Appendix: source

Thrown at pkg/api/api_impl.go:258

	}
}

func validateTreeShaking(value TreeShaking, bundle bool, format Format) bool {
	switch value {
	case TreeShakingDefault:
		// If we're in an IIFE then there's no way to concatenate additional code
		// to the end of our output so we assume tree shaking is safe. And when
		// bundling we assume that tree shaking is safe because if you want to add
		// code to the bundle, you should be doing that by including it in the
		// bundle instead of concatenating it afterward, so we also assume tree
		// shaking is safe then. Otherwise we assume tree shaking is not safe.
		return bundle || format == FormatIIFE
	case TreeShakingFalse:
		return false
	case TreeShakingTrue:
		return true
	default:
		panic("Invalid tree shaking")
	}
}

func validateLoader(value Loader) config.Loader {
	switch value {
	case LoaderBase64:
		return config.LoaderBase64
	case LoaderBinary:
		return config.LoaderBinary
	case LoaderCopy:
		return config.LoaderCopy
	case LoaderCSS:
		return config.LoaderCSS
	case LoaderDataURL:
		return config.LoaderDataURL
	case LoaderDefault:
		return config.LoaderDefault
	case LoaderEmpty:

View on GitHub (pinned to f6058f8364)