evanw/esbuild · critical

Invalid target

Error message

Invalid target

What it means

validateFeatures (pkg/api/api_impl.go:350) panics when BuildOptions.Target is not DefaultTarget(0) nor one of ESNext/ES5/ES2015..ES2026. The target is translated into a compat.Semver constraint used to disable unsupported JS/CSS features; an unknown target cannot be mapped to a feature set, so esbuild aborts. Reached from validateBuildOptions via validateFeatures before engines are processed.

Solutions

  1. Use DefaultTarget (let esbuild choose) or one of the declared ES* constants that your esbuild version supports; ESNext avoids feature-gating entirely.
  2. If loading target from a string, map "es2022" etc. to constants at load and reject unknown strings with a clear error.
  3. Bounds-check decoded integers against 0..14 (the enum length at this version) before casting.
  4. Upgrade esbuild (and rebuild bridges) to a version that supports the target you need.

Example fix

// before
opts := api.BuildOptions{Target: api.Target(20)}

// after
opts := api.BuildOptions{Target: api.DefaultTarget}
Defensive patterns

Strategy: validation

Validate before calling

func checkTarget(t api.Target) error {
    switch t {
    case api.DefaultTarget, api.ESNext, api.ES5,
        api.ES2015, api.ES2016, api.ES2017, api.ES2018, api.ES2019,
        api.ES2020, api.ES2021, api.ES2022, api.ES2023, api.ES2024,
        api.ES2025, api.ES2026:
        return nil
    }
    return fmt.Errorf("invalid target %d", uint8(t))
}

Type guard

func isValidTarget(t api.Target) bool {
    switch t {
    case api.DefaultTarget, api.ESNext, api.ES5,
        api.ES2015, api.ES2016, api.ES2017, api.ES2018, api.ES2019,
        api.ES2020, api.ES2021, api.ES2022, api.ES2023, api.ES2024,
        api.ES2025, api.ES2026:
        return true
    }
    return false
}

Prevention

When it happens

Trigger: Passing Target as a raw integer outside the declared enum, deserializing it from a numeric config, or referencing a constant that does not exist in this esbuild version (e.g. ES2027 against an older binary). The panic fires during options validation, before any file is scanned.

Common situations: Running against an older esbuild than your config targets (new ES20xx constants are added over time), version drift between the JS API and the Go core after re-vendoring, or a typo such as `Target: api.ES2020Stable`.

Related errors


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

Appendix: source

Thrown at pkg/api/api_impl.go:350

	case ES2019:
		constraints[compat.ES] = compat.Semver{Parts: []int{2019}}
	case ES2020:
		constraints[compat.ES] = compat.Semver{Parts: []int{2020}}
	case ES2021:
		constraints[compat.ES] = compat.Semver{Parts: []int{2021}}
	case ES2022:
		constraints[compat.ES] = compat.Semver{Parts: []int{2022}}
	case ES2023:
		constraints[compat.ES] = compat.Semver{Parts: []int{2023}}
	case ES2024:
		constraints[compat.ES] = compat.Semver{Parts: []int{2024}}
	case ES2025:
		constraints[compat.ES] = compat.Semver{Parts: []int{2025}}
	case ES2026:
		constraints[compat.ES] = compat.Semver{Parts: []int{2026}}
	case ESNext, DefaultTarget:
	default:
		panic("Invalid target")
	}

	for _, engine := range engines {
		if match := versionRegex.FindStringSubmatch(engine.Version); match != nil {
			if major, err := strconv.Atoi(match[1]); err == nil {
				parts := []int{major}
				if minor, err := strconv.Atoi(match[2]); err == nil {
					parts = append(parts, minor)
					if patch, err := strconv.Atoi(match[3]); err == nil {
						parts = append(parts, patch)
					}
				}
				name := convertEngineName(engine.Name)
				new := compat.Semver{
					Parts:      parts,
					PreRelease: match[4],
				}
				if old, ok := constraints[name]; ok && compat.CompareSemver(old, new) < 0 {

View on GitHub (pinned to f6058f8364)