sveltejs/svelte · error · TypeError

value is not async iterable

Error message

value is not async iterable

What it means

In DEV, Svelte wraps `for await` loops with `for_await_track_reactivity_loss` to warn when reactivity is lost across async boundaries. This TypeError is thrown when the value handed to a `for await...of` loop is neither async-iterable (no `Symbol.asyncIterator`) nor sync-iterable (no `Symbol.iterator`) — both probes return `undefined`. It mirrors native JS `for await` semantics but surfaces inside Svelte's reactivity-tracking layer.

Source

Thrown at packages/svelte/src/internal/client/reactivity/async.js:233

 * after the `async_iterator` return resolves (if it runs)
 * @template T
 * @template TReturn
 * @param {Iterable<T> | AsyncIterable<T>} iterable
 * @returns {AsyncGenerator<T, TReturn | undefined>}
 */
export async function* for_await_track_reactivity_loss(iterable) {
	// This is based on the algorithms described in ECMA-262:
	// ForIn/OfBodyEvaluation
	// https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
	// AsyncIteratorClose
	// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-asynciteratorclose

	/** @type {AsyncIterator<T, TReturn>} */
	// @ts-ignore
	const iterator = iterable[Symbol.asyncIterator]?.() ?? iterable[Symbol.iterator]?.();

	if (iterator === undefined) {
		throw new TypeError('value is not async iterable');
	}

	// eslint-disable-next-line no-useless-assignment
	let invoke_return = true;

	try {
		while (true) {
			const { done, value } = (await track_reactivity_loss(iterator.next()))();
			if (done) {
				invoke_return = false;
				break;
			}
			var prev = reactivity_loss_tracker;
			try {
				yield value;
			} catch (e) {
				set_reactivity_loss_tracker(prev);
				// If the yield throws, we need to call `return` but not return its value, instead rethrow

View on GitHub (pinned to 20b341f100)

Solutions

  1. Iterate a true async iterable: a ReadableStream's `.body`, an async generator, or an array (which has `Symbol.iterator`).
  2. Guard against null/undefined with a default: `for await (const x of data ?? [])`.
  3. For `fetch`, iterate `response.body` (async-iterable in modern runtimes), not the `Response` object.

Example fix

// before
async function load() {
	for await (const chunk of res) { /* res is a Response, not iterable */ }
}
// after
async function load() {
	for await (const chunk of res.body) { /* ReadableStream is async iterable */ }
}
Defensive patterns

Strategy: validation

Validate before calling

function isAsyncIterable(v) {
	return v != null && (typeof v[Symbol.asyncIterator] === 'function' || typeof v[Symbol.iterator] === 'function');
}
if (!isAsyncIterable(data)) throw new Error('Expected an async iterable');
for await (const x of data) { /* ... */ }

Type guard

function isAsyncIterable(v) {
	return v != null && (typeof v[Symbol.asyncIterator] === 'function' || typeof v[Symbol.iterator] === 'function');
}

Prevention

When it happens

Trigger: A `for await (const x of expr)` loop where `expr` is a non-iterable value: a raw `fetch` `Response` (instead of `response.body`), `null`, a number, or a plain object. The function probes `iterable[Symbol.asyncIterator]` then `iterable[Symbol.iterator]`; if both yield `undefined`, it throws.

Common situations: Iterating a `fetch` Response directly instead of `.body`; awaiting a value that resolves to a non-iterable; returning a plain object from an async function and looping over it; DEV-mode only (production throws the native TypeError).

Related errors


AI-assisted analysis of sveltejs/svelte@20b341f100 (2026-08-12). Data as JSON: /api/errors/b5887780115d9370. Report an issue: GitHub.