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

  1. Upgrade swc_ecma_minifier/swc_core: non-convergence regressions are treated as bugs and fixed quickly
  2. Cap `compress.passes` to a small number (e.g. 2) so the loop terminates before the guard trips
  3. Disable the transforms most often involved in oscillation (collapse_vars, inline/sequences, unsafe options) to break the cycle
  4. 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

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


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