evanw/esbuild · critical

Invalid packages

Error message

Invalid packages

What it means

validateExternalPackages (pkg/api/api_impl.go:239) panics when Packages is not PackagesDefault(0), PackagesBundle(1), or PackagesExternal(2). The returned bool tells the resolver whether `node_modules` dependencies are inlined or treated as external imports; an undefined value would break module resolution, so esbuild aborts. Called from validateBuildOptions.

Solutions

  1. Use api.PackagesDefault (default), api.PackagesBundle, or api.PackagesExternal only.
  2. Bounds-check externally sourced integers against 0..2 before casting.
  3. Represent the option as a string in serialized config and map to the constant at load.
  4. Pin and rebuild against one esbuild version across your stack.

Example fix

// before
opts := api.BuildOptions{Packages: api.Packages(3), Bundle: true}

// after
opts := api.BuildOptions{Packages: api.PackagesDefault, Bundle: true}
Defensive patterns

Strategy: validation

Validate before calling

func checkPackages(p api.Packages) error {
    switch p {
    case api.PackagesDefault, api.PackagesBundle, api.PackagesExternal:
        return nil
    }
    return fmt.Errorf("invalid packages %d (want 0..2)", uint8(p))
}

Type guard

func isValidPackages(p api.Packages) bool {
    switch p {
    case api.PackagesDefault, api.PackagesBundle, api.PackagesExternal:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Assigning BuildOptions.Packages from a raw integer or a deserialized value outside 0..2. The panic fires during option validation, before any resolution.

Common situations: Bundlers/wrappers that expose a numeric `--packages` mode, version skew between JS API and Go core after an upgrade, or hand-written config using an invented constant name.

Related errors


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

Appendix: source

Thrown at pkg/api/api_impl.go:239

func validateASCIIOnly(value Charset) bool {
	switch value {
	case CharsetDefault, CharsetASCII:
		return true
	case CharsetUTF8:
		return false
	default:
		panic("Invalid charset")
	}
}

func validateExternalPackages(value Packages) bool {
	switch value {
	case PackagesDefault, PackagesBundle:
		return false
	case PackagesExternal:
		return true
	default:
		panic("Invalid packages")
	}
}

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:

View on GitHub (pinned to f6058f8364)