evanw/esbuild · critical

Invalid target

Error message

Invalid target

What it means

A panic raised by validateFeatures when the Target value is not one of the documented ECMA targets (ES5, ES2015..ES2025, ESNext, DefaultTarget). Reachable only through Go-API misuse that constructs an out-of-range Target; the JS API's Target union ('es5' | 'es2015' | ... | 'esnext') maps to valid constants and cannot trigger it.

Source

Thrown at pkg/api/api_impl.go:335

	case ES2018:
		constraints[compat.ES] = compat.Semver{Parts: []int{2018}}
	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 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)
					}
				}
				constraints[convertEngineName(engine.Name)] = compat.Semver{
					Parts:      parts,
					PreRelease: match[4],
				}
				continue
			}

View on GitHub (pinned to 6ff1d8b0d8)

Solutions

  1. Use only the documented Target constants (ES5, ES2015..ES2025, ESNext, DefaultTarget).
  2. For a future/unknown target, fall back to ESNext rather than casting a number.
  3. Validate deserialized target values against an allowlist.
  4. Pin a single esbuild version and re-check target support after upgrades.

Example fix

// before
opts.Target = api.Target(123)

// after
opts.Target = api.ES2022  // or ESNext for bleeding-edge
Defensive patterns

Strategy: type-guard

Validate before calling

func validTarget(t api.Target) bool {
  switch t {
  case api.DefaultTarget, 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.ESNext:
    return true
  }
  return false
}

Type guard

type Target = 'es5' | 'es2015' | 'es2016' | 'es2017' | 'es2018' | 'es2019' | 'es2020' | 'es2021' | 'es2022' | 'es2023' | 'es2024' | 'es2025' | 'esnext'
const TARGETS = new Set<Target>(['es5','es2015','es2016','es2017','es2018','es2019','es2020','es2021','es2022','es2023','es2024','es2025','esnext'])
function isTarget(v: unknown): v is Target {
  return typeof v === 'string' && (TARGETS as Set<string>).has(v)
}

Prevention

When it happens

Trigger: Go API: BuildOptions.Target = Target(123). The JS API cannot reach this panic. Fires inside validateFeatures during the target/engine compatibility computation.

Common situations: Reflective config decoding; fork ordinal drift; unsafe casts; passing a numeric target from a roadmap string like 'es2030' that esbuild does not yet know.

Related errors


AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03). Data as JSON: /data/errors/c7304a3391daf108.json. Report an issue: GitHub.