swc-project/swc · error · TypeError

Invalid attempt to iterate non-iterable instance. In order t

Error message

Invalid attempt to iterate non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.

What it means

The napi `minify`/`minify_sync` entry point accepts either raw code or a JSON map of filename to code; the caller signals the JSON form with the `is_json: bool` flag. When is_json is true, MinifyTarget::Json deserializes the input with serde_json::from_str(...).expect("Invalid JSON"), so malformed JSON panics the Rust side instead of returning a structured napi error. The panic is surfaced by napi-rs as a JS exception with the message 'Invalid JSON'.

Source

Thrown at crates/swc_ecma_transforms_base/src/helpers/generated/_create_for_of_iterator_helper_loose.rs:27

    #[cfg(feature = "inline-helpers")]
    source: r#"function _create_for_of_iterator_helper_loose(o, allowArrayLike) {
    var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];

    if (it) return (it = it.call(o)).next.bind(it);
    // Fallback for engines without symbol support
    if (Array.isArray(o) || (it = _unsupported_iterable_to_array(o)) || allowArrayLike && o && typeof o.length === "number") {
        if (it) o = it;

        var i = 0;

        return function() {
            if (i >= o.length) return { done: true };

            return { done: false, value: o[i++] };
        };
    }

    throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
"#,
    #[cfg(feature = "inline-helpers")]
    deps: super::HelperBitmap::from_bits(0x00000004000000000000020000000008),
};

#[cfg(feature = "inline-helpers")]
pub fn stmts() -> &'static [swc_ecma_ast::Stmt] {
    static STMTS: once_cell::sync::Lazy<Vec<swc_ecma_ast::Stmt>> =
        once_cell::sync::Lazy::new(|| super::super::parse(DEF.source, DEF.import_path));
    &STMTS
}

View on GitHub (pinned to 5176682b65)

Solutions

  1. If you have plain source code, call minifySync(code, opts, false, extras) - the third argument selects the single-string mode.
  2. If you want a filename attached, pass JSON.stringify({ 'input.js': code }) as the first argument and keep is_json=true.
  3. Ensure the JSON map has exactly one entry - the next line asserts codes.len() == 1 ('swc.minify does not support concatting multiple files yet').
  4. Validate/parse the JSON in JS before calling the binding so you get a normal SyntaxError with position instead of a Rust panic.

Example fix

// before: raw code passed with is_json=true -> panic 'Invalid JSON'
const out = minifySync(code, opts, true, { mangleNameCache: null });

// after: plain code mode
const out = minifySync(code, opts, false, { mangleNameCache: null });
// or: single-entry filename map
const out = minifySync(JSON.stringify({ 'input.js': code }), opts, true, {
  mangleNameCache: null,
});
Defensive patterns

Strategy: validation

Validate before calling

// Validate the JSON map before hitting the Rust binding:
function toMinifyJson(filename, code) {
  const map = { [filename]: code };
  const json = JSON.stringify(map); // throws SyntaxError early if you built it wrong
  const parsed = JSON.parse(json);
  const entries = Object.entries(parsed);
  if (entries.length !== 1 || typeof entries[0][1] !== 'string') {
    throw new TypeError('expected exactly one { [file]: code } entry');
  }
  return json;
}
const out = minifySync(toMinifyJson('input.js', code), opts, true, {
  mangleNameCache: null,
});

Type guard

function isMinifyJsonTarget(v: unknown): v is Record<string, string> {
  return (
    typeof v === 'object' &&
    v !== null &&
    !Array.isArray(v) &&
    Object.keys(v).length === 1 &&
    Object.values(v).every((x) => typeof x === 'string')
  );
}

Try / catch

try {
  out = minifySync(json, opts, true, extras);
} catch (e) {
  // napi surfaces the Rust panic as a JS Error with message 'Invalid JSON'
  if (/Invalid JSON/.test(String(e?.message))) {
    throw new SyntaxError(`minify input is not a single-entry JSON map: ${json.slice(0, 80)}`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling minifySync(code, opts, true, extras) (or the async minify) where `code` is not a valid JSON object of shape { [filename]: string } - e.g. raw JS source passed while is_json=true, truncated JSON buffers, single quotes, trailing commas, or a JSON object with non-string values.

Common situations: Mixing up argument order or the boolean flag when migrating from @swc/core to the lower-level binding_core_node API; passing a filename instead of a JSON map; JSON.stringify omissions when the map is built manually; a second assertion in the same function also fires when the map has more than one entry.

Related errors


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