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
- Use api.PackagesDefault (default), api.PackagesBundle, or api.PackagesExternal only.
- Bounds-check externally sourced integers against 0..2 before casting.
- Represent the option as a string in serialized config and map to the constant at load.
- 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
- Use PackagesDefault; it applies esbuild's recommended behavior per platform.
- Map CLI-style strings to constants rather than ordinals.
- Bounds-check integers from external sources to 0..2.
- Keep wrappers and the core on the same esbuild version.
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)