denoland/deno · error · Error
ERR_INVALID_STATE
ERR_INVALID_STATE
Error message
Invalid state: The iterator method must return an object
What it means
After getIterator() locates an iterator method, the WHATWG protocol requires calling it to return an iterator OBJECT. If obj[Symbol.iterator]() (or the async equivalent) returns a primitive (number, string, boolean) or null, Deno's node:stream/web machinery throws ERR_INVALID_STATE ('The iterator method must return an object') rather than proceeding.
Source
Thrown at ext/node/polyfills/internal/webstreams/util.js:249
if (method == null) {
const syncMethod = obj[SymbolIterator];
if (syncMethod === undefined) {
throw new ERR_ARG_NOT_ITERABLE(obj);
}
return createAsyncFromSyncIterator(
getIterator(obj, "sync", syncMethod),
);
}
} else {
method = obj[SymbolIterator];
}
}
if (method === undefined) {
throw new ERR_ARG_NOT_ITERABLE(obj);
}
const iterator = FunctionPrototypeCall(method, obj);
if (typeof iterator !== "object" || iterator === null) {
throw new ERR_INVALID_STATE("The iterator method must return an object");
}
return { iterator, nextMethod: iterator.next, done: false };
}
function iteratorNext(iteratorRecord, value) {
const result = value === undefined
? FunctionPrototypeCall(iteratorRecord.nextMethod, iteratorRecord.iterator)
: FunctionPrototypeCall(
iteratorRecord.nextMethod,
iteratorRecord.iterator,
value,
);
if (typeof result !== "object" || result === null) {
throw new ERR_INVALID_STATE(
"The iterator.next() method must return an object",
);
}
return result;View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Make the iterator method return an object — the simplest fix is delegating to a generator: [Symbol.iterator]() { return this.values(); } where values is a generator
- Or return the object itself with a next() method: [Symbol.iterator]() { let i = 0; const self = this; return { next: () => ({ value: self.items[i], done: i++ >= self.items.length }) }; }
- Smoke-test custom iterables with [...obj] or Array.from(obj) before feeding them to streams
Example fix
// before
const seq = { [Symbol.iterator]: () => this.items }; // returns an array in wrong `this` context or a primitive
// after
const seq = { *[Symbol.iterator]() { yield* this.items; } }; Defensive patterns
Strategy: validation
Validate before calling
const iterFn = (source as any)?.[Symbol.iterator];
if (typeof iterFn !== 'function') throw new TypeError('not iterable');
const iter = iterFn.call(source);
if (typeof iter !== 'object' || iter === null) {
throw new TypeError('Symbol.iterator must return an iterator object');
}
ReadableStream.from(source); Type guard
function iteratorMethodIsSound(source: unknown): boolean {
const fn = (source as any)?.[Symbol.iterator];
if (typeof fn !== 'function') return false;
const it = fn.call(source);
return it != null && typeof it === 'object';
} Try / catch
try {
rs = ReadableStream.from(source);
} catch (e: any) {
if (e?.code === 'ERR_INVALID_STATE' && /iterator method/.test(e.message)) {
rs = ReadableStream.from([...makeIterable(source)]); // rebuild via generator
} else throw e;
} Prevention
- Implement Symbol.iterator by delegating to a generator — never return a primitive or this.someField
- Unit-test custom iterables with [...obj] before passing them to stream APIs
- Never stub Symbol.iterator with () => null in mocks
When it happens
Trigger: A custom iterable whose [Symbol.iterator] returns a raw value: { [Symbol.iterator]: () => 42 }; an implementation returning this.value where value is a primitive; test mocks stubbing Symbol.iterator with () => null; a factory method that forgot 'return this' / 'return generator()'.
Common situations: Hand-rolled iterable protocol implementations; mocking libraries replacing Symbol.iterator with trivial stubs; refactoring an iterable class and dropping the iterator-returning statement.
Related errors
- ERR_ARG_NOT_ITERABLE
- ERR_INVALID_ARG_VALUE
- BenchContext::start() has already been invoked
- ${prefix}Linter plugin name must only contain lowercase lett
- ${prefix}Linter plugin name must start and end with a lowerc
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/dc90ec2ec378ab8b.
Report an issue: GitHub.