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 rethrowView on GitHub (pinned to 20b341f100)
Solutions
- Iterate a true async iterable: a ReadableStream's `.body`, an async generator, or an array (which has `Symbol.iterator`).
- Guard against null/undefined with a default: `for await (const x of data ?? [])`.
- 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
- Validate iterables before `for await`.
- Default to empty arrays with `?? []`.
- Iterate `response.body`, not the `Response` object.
- Remember this DEV wrapper adds reactivity-loss tracking on top of native semantics.
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.