swc-project/swc · error

`{raw}` is not a valid ecmascript version

Error message

`{raw}` is not a valid ecmascript version

What it means

Converting a terser ecma version to EsVersion first normalizes the numeric value via normalize_terser_ecma_num: only 3, 5, 6 (mapped to es2015), and full years 2015+ are valid. Any other number (0, 1, 2, 4, 7, 8, 9, 1999, ...) returns None and panics with '`{raw}` is not a valid ecmascript version'. String values that parse as a number go through the same path.

Source

Thrown at crates/swc_ecma_minifier/src/option/terser.rs:464

    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TerserEcmaVersion::Num(value) => value.fmt(f),
            TerserEcmaVersion::Str(value) => value.fmt(f),
        }
    }
}

impl From<TerserEcmaVersion> for EsVersion {
    fn from(value: TerserEcmaVersion) -> Self {
        let raw = value.to_string();
        let normalized = match value {
            TerserEcmaVersion::Num(value) => normalize_terser_ecma_num(value),
            TerserEcmaVersion::Str(value) => match value.parse::<usize>() {
                Ok(value) => normalize_terser_ecma_num(value),
                Err(..) => Some(value),
            },
        }
        .unwrap_or_else(|| panic!("`{raw}` is not a valid ecmascript version"));

        serde_json::from_value(Value::String(normalized))
            .unwrap_or_else(|_| panic!("`{raw}` is not a valid ecmascript version"))
    }
}

fn normalize_terser_ecma_num(value: usize) -> Option<String> {
    match value {
        3 | 5 => Some(format!("es{value}")),
        6 => Some(String::from("es2015")),
        2015.. => Some(format!("es{value}")),
        _ => None,
    }
}

impl From<TerserTopRetainOption> for Vec<Atom> {
    fn from(v: TerserTopRetainOption) -> Self {
        match v {

View on GitHub (pinned to 5176682b65)

Solutions

  1. Use an accepted numeric form: 5, 6, or a full year such as 2015, 2020
  2. Prefer the canonical string form ("es2015", "es2020") to make intent explicit and avoid the numeric special cases
  3. Validate the ecma field against the accepted set at config load time

Example fix

// before
{ "compress": { "ecma": 7 } } // panic: `7` is not a valid ecmascript version

// after
{ "compress": { "ecma": 2015 } }
Defensive patterns

Strategy: validation

Validate before calling

// JS: accepted numeric ecma values
const okNum = n => [3, 5, 6].includes(n) || (Number.isInteger(n) && n >= 2015);
if (!okNum(ecma)) throw new Error(`ecma ${ecma} is not valid; use 5, 6, or a year >= 2015`);

Prevention

When it happens

Trigger: compress option ecma set to 7, 8, or 9 (or "7"): position between ES6 and the year-based scheme (es2015+); ecma: 4 or 0; any pre-2015 year other than the special-cased 3/5/6.

Common situations: Guessing that terser accepts 7/8/9 shorthands (it only accepts 5, 2015+, or 6-as-2015), porting configs from tools that use plain engine version numbers, programmatic config generation inserting engine major versions.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/d69218198a6ca6ae. Report an issue: GitHub.