{"record":{"id":"4937de59f243d355","repo":"ReactiveX/rxjs","slug":"iterator-next-must-return-an-object","errorCode":null,"errorMessage":"Iterator next() must return an Object","messagePattern":"Iterator next\\(\\) must return an Object","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/observable-polyfill/src/index.ts","lineNumber":443,"sourceCode":"\n    if (!subscriber.active) {\n      return;\n    }\n\n    let finished = false;\n    const closeIterator: Teardown = () => {\n      if (!finished) {\n        closeSyncIterator(record, subscriber.signal.reason);\n      }\n    };\n    closeIterator[propagateTeardownError] = true;\n    subscriber.addTeardown(closeIterator);\n\n    try {\n      while (subscriber.active) {\n        const result = record.next.call(record.iterator);\n        if (!isObject(result)) {\n          throw new TypeError('Iterator next() must return an Object');\n        }\n\n        const iteratorResult = result as IteratorResult<T>;\n        if (iteratorResult.done) {\n          finished = true;\n          subscriber.complete();\n          return;\n        }\n        subscriber.next(iteratorResult.value);\n      }\n    } catch (error) {\n      subscriber.error(error);\n    }\n  });\n}\n\nfunction fromAsyncIterable<T>(ObservableCtor: typeof Observable<T>, value: object): Observable<T> {\n  return new ObservableCtor((subscriber) => {","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/observable-polyfill/src/index.ts#L425-L461","documentation":"While pulling values from a sync iterable inside fromIterable, each call to iterator.next() must return an object of the shape { done, value }. If a call yields a primitive (or undefined), the polyfill throws this TypeError to enforce the iterator result contract, and the subscriber errors out after closing the iterator.","triggerScenarios":"Observable.from(customIterable) where the iterator's next() returns undefined, a bare value, or another non-object at some point during iteration (often after exhaustion).","commonSituations":"Custom iterators that return the value directly instead of { value, done }; state-machine iterators with a missing/typo'd terminal branch (return; instead of return { done: true }); test doubles returning null.","solutions":["Fix next() to always return an object: { value, done: boolean } including the final { done: true }.","Prefer generators (function*) which produce conforming results automatically.","Audit exhaustion paths — the most common bug is returning undefined when finished.","Validate with a quick loop before passing to from(): for (const _ of iterable) break;"],"exampleFix":"// before\nnext() { return this.i < 3 ? this.values[this.i++] : undefined; }\n// after\nnext() { return this.i < 3 ? { value: this.values[this.i++], done: false } : { value: undefined, done: true }; }","handlingStrategy":"validation","validationCode":"const it = iterable[Symbol.iterator]();\nconst r = it.next();\nif (!(r instanceof Object)) throw new Error('iterator next() must return an object');","typeGuard":"function isValidIteratorResult(r: unknown): r is IteratorResult<unknown> {\n  return r instanceof Object;\n}","tryCatchPattern":"Observable.from(iterable).subscribe({ error: e => { if (e instanceof TypeError && /must return an Object/.test(e.message)) { /* fix iterator */ } } });","preventionTips":["Always return { value, done } from next().","Use generators instead of hand-rolled iterators.","Add a `for...of` smoke test over custom iterables before passing them to from()."],"tags":["iterator","iterator-next","observable-from","typeerror","protocol"],"backgroundTag":"invalid-iterator-protocol","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}