How to fix TypeError: Iterator result must be an object (stream implementation error) in Node.js

Node.jsINTERMEDIATEMEDIUMUpdated Dec 22, 2025· 129 views

This error occurs when a custom iterator's next() method returns a non-object value instead of the required {value, done} object. Common in stream implementations and async iterators.

Quick fix

Fixes TypeError: Iterator result must be an object (stream implementation error)

// Incorrect - returns primitive
const badIterator = {
  current: 0,
  next() {
    return this.current++; // ❌ Returns number
  }
};

// Correct - returns iterator result object
const goodIterator = {
  current: 0,
  next() {
    const value = this.current++;
    const done = value >= 10;
    return { value, done }; // ✅ Returns object
  }
};
  1. Verify iterator next() method returns an object
  2. Fix async iterator to return proper objects
  3. Fix stream iterator implementation
  4. Use generator functions for simpler implementation
node.js — error
// Incorrect - returns primitive
const badIterator = {
current: 0,
next() {
return this.current++; // ❌ Returns number
}
};
// Correct - returns iterator result object
const goodIterator = {
current: 0,
next() {
const value = this.current++;
const done = value >= 10;
return { value, done }; // ✅ Returns object
}
};
TypeError: Iterator result must be an object (stream implementation error)

What this error means

This TypeError is thrown when a custom iterator implementation violates the JavaScript iterator protocol. According to the iterator protocol, the next() method must return an object with two properties: 'value' (the current item) and 'done' (a boolean indicating if iteration is complete). In Node.js streams, this error frequently appears when implementing custom async iterators or when using ReadableStream.from() with an improperly implemented iterable. The stream implementation expects iterator results to be objects, but receives null, undefined, or primitive values instead. The error occurs at the JavaScript engine level when it validates the return value of an iterator's next() method during iteration. This is a fundamental requirement of the iterator protocol that applies to both synchronous and asynchronous iterators.

Typical symptoms

  • TypeError thrown during stream iteration or for-await-of loops
  • Custom stream implementation crashes when consumed
  • ReadableStream.from() or Readable.from() throws immediately
  • Pipeline operations fail with iterator-related errors
  • Async iterator consumption fails with type validation error

Common causes

  1. Custom iterator next() method returns null or undefined instead of an object
  2. Iterator next() method returns a primitive value (string, number, boolean)
  3. Async iterator next() method resolves to a non-object value
  4. Missing return statement in iterator next() method
  5. Incorrect object structure missing value or done properties
  6. Stream implementation returns raw values without wrapping in iterator result object

How to fix "TypeError: Iterator result must be an object (stream implementation error)"

1Verify iterator next() method returns an object

Check that your custom iterator's next() method always returns an object with the required structure:

javascript
// Incorrect - returns primitive
const badIterator = {
  current: 0,
  next() {
    return this.current++; // ❌ Returns number
  }
};

// Correct - returns iterator result object
const goodIterator = {
  current: 0,
  next() {
    const value = this.current++;
    const done = value >= 10;
    return { value, done }; // ✅ Returns object
  }
};

Ensure both value and done properties are present in the returned object.

2Fix async iterator to return proper objects

For async iterators, ensure the Promise resolves to a valid iterator result object:

javascript
// Incorrect async iterator
async function* badAsyncGen() {
  yield null; // ❌ Yields null
}

// Correct async iterator
async function* goodAsyncGen() {
  yield { value: 'data', done: false }; // ✅ Yields object
  // OR simply yield the value - generator handles wrapping
  yield 'data'; // ✅ Also valid in generators
}

// For custom async iterators (not generators)
const asyncIterator = {
  async next() {
    return { value: await fetchData(), done: false }; // ✅ Returns object
  }
};

Generator functions automatically wrap yielded values, but custom iterators must return the full object structure.

3Fix stream iterator implementation

When implementing custom streams with iterators, ensure proper object returns:

javascript
const { Readable } = require('stream');

// Incorrect - returns raw values
const badStream = Readable.from({
  [Symbol.asyncIterator]() {
    let count = 0;
    return {
      async next() {
        if (count++ < 3) return 'data'; // ❌ Returns string
        return null; // ❌ Returns null
      }
    };
  }
});

// Correct - returns proper iterator result objects
const goodStream = Readable.from({
  [Symbol.asyncIterator]() {
    let count = 0;
    return {
      async next() {
        if (count++ < 3) {
          return { value: 'data', done: false }; // ✅ Proper object
        }
        return { value: undefined, done: true }; // ✅ End of iteration
      }
    };
  }
});
4Use generator functions for simpler implementation

Instead of manually creating iterator objects, use generator functions which handle the protocol automatically:

javascript
// Manual iterator (error-prone)
function createIterator() {
  let i = 0;
  return {
    next() {
      // Easy to forget proper return structure
      return { value: i++, done: i > 10 };
    }
  };
}

// Generator function (safer)
function* createGenerator() {
  let i = 0;
  while (i <= 10) {
    yield i++; // Automatically wrapped in {value, done}
  }
}

// Async generator for streams
async function* createAsyncGenerator() {
  for (let i = 0; i < 10; i++) {
    yield await fetchData(i); // Automatically wrapped
  }
}

const stream = Readable.from(createAsyncGenerator());

Generators automatically handle the iterator protocol, reducing the chance of returning invalid objects.

5Add type checking for custom iterators

Add validation to catch iterator result errors early in development:

javascript
function validateIteratorResult(result) {
  if (typeof result !== 'object' || result === null) {
    throw new TypeError('Iterator result must be an object');
  }
  if (!('value' in result && 'done' in result)) {
    throw new TypeError('Iterator result must have value and done properties');
  }
  return result;
}

const safeIterator = {
  current: 0,
  next() {
    const result = {
      value: this.current++,
      done: this.current > 10
    };
    return validateIteratorResult(result);
  }
};

For TypeScript, define proper return types:

typescript
interface Iterator<T> {
  next(): IteratorResult<T>;
}

interface IteratorResult<T> {
  value: T;
  done: boolean;
}

Advanced notes

Iterator Protocol Requirements: The JavaScript iterator protocol is strict - the next() method must return an object with 'value' and 'done' properties. While 'value' can be any type (including undefined), the return itself must be an object. Returning undefined, null, or primitives directly violates the protocol.

Async Iterator Differences: Async iterators return Promises that resolve to iterator result objects. The promise itself must resolve to an object - returning a promise that resolves to null or undefined will trigger this error.

Stream Implementation Context: Node.js streams use async iterators internally. When you implement Symbol.asyncIterator or use Readable.from() with a custom iterable, the stream machinery validates iterator results. This error often surfaces during stream consumption rather than creation.

Generator Advantages: Generator functions (function*) and async generator functions (async function*) automatically handle the iterator protocol. When you yield a value, the generator runtime wraps it in {value: yourValue, done: false}. Manual iterator implementations require explicit object returns.

Common Mistake Pattern: A frequent error is conditionally returning different types:

javascript
next() {
  if (this.hasData) return { value: this.data, done: false };
  return null; // ❌ Should return { value: undefined, done: true }
}

Debugging Tip: Use console.log() or a debugger to inspect what your next() method actually returns before the error occurs. The issue is always in the return value type, not the logic.

Frequently asked questions

What does "TypeError: Iterator result must be an object (stream implementation er" mean?
This TypeError is thrown when a custom iterator implementation violates the JavaScript iterator protocol. According to the iterator protocol, the next() method must return an object with two properties: 'value' (the current item) and 'done' (a boolean indicating if iteration is complete). from() with an improperly implemented iterable.
How do I fix "TypeError: Iterator result must be an object (stream implementation er"?
This error occurs when a custom iterator's next() method returns a non-object value instead of the required {value, done} object. Common in stream implementations and async iterators.
What causes "TypeError: Iterator result must be an object (stream implementation er"?
Custom iterator next() method returns null or undefined instead of an object Iterator next() method returns a primitive value (string, number, boolean) Async iterator next() method resolves to a non-object value
Is the "TypeError: Iterator result must be an object (stream implementation er" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading