vitejs/vite · error · Error

Unsupported target

Error message

Unsupported target "${entry}"

What it means

Thrown by `convertTargets` after ES-year expansion when iterating individual target entries: the entry is not `esnext`, does not match a known browser prefix in `map` (chrome, edge, firefox, ie, ios, node, opera, rhino, safari), or matches a browser `map` marks false (node, hermes, rhino) without further handling, or its version portion is unparseable. css.ts:3652 throws with the entry.

Solutions

  1. Use only supported browser targets with version: chrome, edge, firefox, ie, ios, opera, safari (e.g. 'chrome110', 'safari16').
  2. For node/rhino/hermes targets that map=false, ensure the prefix is recognized so they are skipped, or remove them.
  3. Replace 'deno'/'bun'/'chrome-android' with their closest supported equivalents (e.g. 'chrome' for chromium-based).
  4. Set `build.target: 'esnext'` or `'baseline-widely-available'` to avoid manual browser targeting.
  5. Disable Lightning CSS (`css.transformer` default) if you must keep unsupported targets.

Example fix

// before
export default defineConfig({
  build: { target: ['deno', 'chrome107'] },
})

// after
export default defineConfig({
  build: { target: ['chrome107', 'firefox110', 'safari16'] },
})
Defensive patterns

Strategy: validation

Validate before calling

const BROWSER_MAP = { chrome: true, edge: true, firefox: true, ie: true, ios: true, opera: true, safari: true, node: true, hermes: true, rhino: true }
function assertBrowserTargets(target: string | string[]) {
  for (const t of Array.isArray(target) ? target : [target]) {
    if (t === 'esnext') continue
    const m = /^([a-z]+)([\d.]+)/.exec(t)
    if (!m || !(m[1] in BROWSER_MAP)) throw new Error(`Unsupported target '${t}'; use chrome/edge/firefox/ie/ios/opera/safari with a version.`)
  }
}

Type guard

function isSupportedBrowserTarget(t: string): boolean {
  if (t === 'esnext') return true
  const m = /^([a-z]+)([\d.]+)/.exec(t)
  return !!m && ['chrome', 'edge', 'firefox', 'ie', 'ios', 'opera', 'safari', 'node', 'hermes', 'rhino'].includes(m[1])
}

Try / catch

try {
  await build()
} catch (e) {
  if (e instanceof Error && /Unsupported target/.test(e.message)) {
    console.error('build.target has an unsupported browser entry; use chrome/edge/firefox/ie/ios/opera/safari with versions.')
  }
  throw e
}

Prevention

When it happens

Trigger: After ES handling, line 3632 loops `entriesWithoutES`; for each entry it slices the alphabetic prefix and looks it up in `map`. If `map[prefix]` is undefined (unknown browser like 'deno', 'bun', 'chrome-android') or the entry's version digits do not parse, line 3652 throws.

Common situations: Setting `build.target: ['deno', 'bun', 'chrome107']`. Using a mobile/Android variant (`chrome-android`, 'safari-ios') not in the map. Typo (`firefo100`). Targets like `node18` (map marks node=false so they are skipped at 3637 only if matched; an unrecognized prefix falls through to 3652).

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/8d6bd11a25bdb9f2. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/node/plugins/css.ts:3652

    const index = entry.search(versionRE)
    if (index >= 0) {
      const browser = map[entry.slice(0, index)]
      if (browser === false) continue // No mapping available
      if (browser) {
        const [major, minor = 0] = entry
          .slice(index)
          .split('.')
          .map((v) => parseInt(v, 10))
        if (!isNaN(major) && !isNaN(minor)) {
          const version = (major << 16) | (minor << 8)
          if (!targets[browser] || version < targets[browser]!) {
            targets[browser] = version
          }
          continue
        }
      }
    }
    throw new Error(`Unsupported target "${entry}"`)
  }

  convertTargetsCache.set(esbuildTarget, targets)
  return targets
}

export function resolveLibCssFilename(
  libOptions: LibraryOptions,
  root: string,
  packageCache?: PackageCache,
): string {
  if (typeof libOptions.cssFileName === 'string') {
    return `${libOptions.cssFileName}.css`
  } else if (typeof libOptions.fileName === 'string') {
    return `${libOptions.fileName}.css`
  }

  const packageJson = findNearestMainPackageData(root, packageCache)?.data

View on GitHub (pinned to b4d66fee14)