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
- Use an accepted numeric form: 5, 6, or a full year such as 2015, 2020
- Prefer the canonical string form ("es2015", "es2020") to make intent explicit and avoid the numeric special cases
- 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
- Use full years (2015+) in configs instead of engine major versions
- Centralize ecma validation in one config helper shared across builds
- Remember 6 maps to es2015; 7-9 do not exist in the terser scheme
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
- failed to parse `global_defs.{k}` of minifier options: {err:
- Value of `global_defs.{k}` must be a string literal:
- failed to parse `pure_funcs` of minifier options: {err:?}
- Error occurred while loading config file at ${config}: ${e}
- {} is not a valid expression
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/d69218198a6ca6ae.
Report an issue: GitHub.