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
- If you have plain source code, call minifySync(code, opts, false, extras) - the third argument selects the single-string mode.
- If you want a filename attached, pass JSON.stringify({ 'input.js': code }) as the first argument and keep is_json=true.
- Ensure the JSON map has exactly one entry - the next line asserts codes.len() == 1 ('swc.minify does not support concatting multiple files yet').
- 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
- Always build the map with JSON.stringify({ [filename]: code }) instead of hand-concatenating strings.
- Keep the is_json flag paired with the input shape: string code -> false, JSON map -> true.
- Pin the binding version and read its .d.ts before upgrading - the boolean arg position has confused migrations.
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
- 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})
- Bindings not found.
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/d0539a682e021cc8.
Report an issue: GitHub.