swc-project/swc · critical
Infinite loop detected (current pass = {}) {}
Error message
Infinite loop detected (current pass = {})
{} What it means
The minifier repeats optimization passes until the program stops changing. A guard panics once self.pass exceeds 200 because the output keeps mutating every pass (non-convergence or oscillation between two rewrites), which would otherwise hang the build forever. The panic embeds a dump of the current program via force_dump_program to make the offending code visible.
Source
Thrown at crates/swc_ecma_minifier/src/compress/mod.rs:139
// );
}
/// Optimize a module. `N` can be [Module] or [FnExpr].
fn optimize_unit(&mut self, n: &mut Program) {
if self.options.passes != 0 && self.options.passes < self.pass {
#[cfg(debug_assertions)]
{
let done = dump(&*n, false);
debug!("===== Done =====\n{}", done);
}
return;
}
// This exists to prevent hanging.
if self.pass > 200 {
let code = force_dump_program(n);
panic!(
"Infinite loop detected (current pass = {})\n{}",
self.pass, code
);
}
#[cfg(all(debug_assertions, feature = "debug"))]
let start = {
let start = force_dump_program(n);
#[cfg(debug_assertions)]
debug!("===== Start =====\n{}", start);
start
};
{
let mut visitor = pure_optimizer(
self.options,
self.marks,
PureOptimizerConfig {View on GitHub (pinned to 5176682b65)
Solutions
- Upgrade swc_ecma_minifier/swc_core: non-convergence regressions are treated as bugs and fixed quickly
- Cap `compress.passes` to a small number (e.g. 2) so the loop terminates before the guard trips
- Disable the transforms most often involved in oscillation (collapse_vars, inline/sequences, unsafe options) to break the cycle
- Save the dumped program from the panic message and file a minimized SWC issue
Example fix
// .swcrc - before
{
"jsc": {
"minify": {
"compress": { "defaults": true, "collapse_vars": true, "inline": 3 }
}
}
}
// .swcrc - after
{
"jsc": {
"minify": {
"compress": { "defaults": true, "passes": 2, "collapse_vars": false }
}
}
} Defensive patterns
Strategy: fallback
Validate before calling
// JS: cap passes before invoking the minifier
const compress = { ...opts.compress, passes: Math.min(opts.compress?.passes ?? 2, 2) }; Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
let out = catch_unwind(AssertUnwindSafe(|| minify(code, &opts)));
if out.is_err() {
// fallback: retry once with a conservative preset that converges
let mut safe = opts.clone();
safe.compress = CompressOptions::default();
out = catch_unwind(AssertUnwindSafe(|| minify(code, &safe)));
}
// if still failing, ship the unminified source and report the dumped program Prevention
- Keep compress.passes small (1-3); convergence issues amplify with passes
- Pin swc_ecma_minifier versions you have fuzz-tested against your bundle corpus
- Cache minified artifacts so a regression cannot wedge CI repeatedly
When it happens
Trigger: Running compress with an option combination where one pass rewrites code that a later pass rewrites back (fixed point never reached); a convergence regression in a specific swc_ecma_minifier version; extremely high user-configured `passes` values interacting with an oscillating transform.
Common situations: Aggressive terser-style compress options in .swcrc or jsc.minify.compress, minifying machine-generated or vendored bundles, upgrading swc_core and hitting a minifier regression, running with defaults:true plus extra toggles.
Related errors
- Invalid attempt to iterate non-iterable instance. In order t
- Duplicated methods (${element.key}) can't be decorated.
- Decorators can't be placed on different accessors with for t
- Duplicated element (${element.key})
- Duplicated element (${elements[j].key})
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/3c7e9c0b556171df.
Report an issue: GitHub.