evanw/esbuild · critical
Invalid platform
Error message
Invalid platform
What it means
esbuild panics in validatePlatform (pkg/api/api_impl.go:117) when BuildOptions.Platform holds a Platform value outside the declared iota enum. Platform is a `uint8` whose only valid values are PlatformDefault(0), PlatformBrowser(1), PlatformNode(2), PlatformNeutral(3); the function switch has no default case, so any other value aborts the process. The panic exists because the public Go API assumes callers pass declared constants, so an out-of-range value signals corruption the bundler cannot safely continue past.
Solutions
- Assign Platform only via the declared constants: api.PlatformDefault / PlatformBrowser / PlatformNode / PlatformNeutral, or leave the field unset to use the zero-value PlatformDefault.
- If deserializing options from JSON/protobuf, validate the integer is in {0,1,2,3} before casting to Platform, or use a string mapping table you control.
- Rebuild/realign the binary and any bridge/generator against the same esbuild commit so enum ordinals match.
- Pin the esbuild version in go.mod / package.json so a future re-vendor cannot silently renumber the iota.
Example fix
// before
opts := api.BuildOptions{Platform: api.Platform(7)}
res := api.Build(opts)
// after
opts := api.BuildOptions{Platform: api.PlatformBrowser}
res := api.Build(opts) Defensive patterns
Strategy: validation
Validate before calling
// validPlatform maps the only acceptable numeric values for api.Platform.
var validPlatform = map[api.Platform]bool{
api.PlatformDefault: true,
api.PlatformBrowser: true,
api.PlatformNode: true,
api.PlatformNeutral:true,
}
func checkPlatform(p api.Platform) error {
if !validPlatform[p] {
return fmt.Errorf("invalid platform %d (want 0..3)", uint8(p))
}
return nil
}
// call before api.Build / api.Context / api.Transform:
if err := checkPlatform(opts.Platform); err != nil { return err } Type guard
func isValidPlatform(p api.Platform) bool {
switch p {
case api.PlatformDefault, api.PlatformBrowser, api.PlatformNode, api.PlatformNeutral:
return true
}
return false
} Prevention
- Never cast an arbitrary integer to api.Platform at a trust boundary; map named strings to constants instead.
- Run a unit test that round-trips every option you serialize to JSON and back, asserting each enum stays in range.
- Pin esbuild in go.mod AND in any JS/wrapper package.json to the same version, and rebuild bridges after upgrades.
- Leave Platform unset to rely on the safe zero-value PlatformDefault when the platform is not important.
When it happens
Trigger: Calling api.Build / api.Context / api.Transform with a BuildOptions struct whose Platform field was assigned from a raw integer (e.g. `Platform: 99`), produced by JSON unmarshalling into a `uint8` cast to Platform, or constructed by a code generator / FFI bridge whose numeric mapping differs from this esbuild version. The panic fires synchronously inside validateBuildOptions -> validatePlatform before any file is read.
Common situations: Mixing esbuild versions (host Go binary built from one checkout, JS/CLI bridge expecting another), hand-rolling a BuildOptions over a serialization boundary, copying example code from a newer docs page against an older binary, or a typo assigning `Platform: PlatformBrowserX`. Also seen when embedding esbuild via cgo and forgetting to map an upstream enum change after a re-vendor.
Related errors
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/cbca968a49aaeb86.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/api_impl.go:117
parts = append(parts, config.PathTemplate{
Data: template,
Placeholder: config.NoPlaceholder,
})
}
return parts
}
func validatePlatform(value Platform) config.Platform {
switch value {
case PlatformDefault, PlatformBrowser:
return config.PlatformBrowser
case PlatformNode:
return config.PlatformNode
case PlatformNeutral:
return config.PlatformNeutral
default:
panic("Invalid platform")
}
}
func validateFormat(value Format) config.Format {
switch value {
case FormatDefault:
return config.FormatPreserve
case FormatIIFE:
return config.FormatIIFE
case FormatCommonJS:
return config.FormatCommonJS
case FormatESModule:
return config.FormatESModule
default:
panic("Invalid format")
}
}
View on GitHub (pinned to f6058f8364)