This error occurs when creating a custom Readable stream class without implementing the required _read() method. Node.js expects all custom Readable streams to define how data is generated and pushed to consumers.
This error indicates that you've created a custom stream by extending the `stream.Readable` class but failed to implement the mandatory `_read()` method. The `_read()` method is the core interface that Node.js uses to pull data from your custom stream implementation. When you extend the Readable class, Node.js expects you to define how data should be generated and provided to consumers. The `_read()` method is called internally by the stream machinery when it needs more data, and it's your responsibility to push data chunks or signal the end of the stream. Without a proper `_read()` implementation, Node.js cannot interact with your custom stream, resulting in this TypeError when the stream attempts to read data.
The most common fix is to add the required _read() method to your custom stream class:
const { Readable } = require('stream');
class MyReadable extends Readable {
constructor(data, options) {
super(options);
this.data = data || [];
this.index = 0;
}
_read() {
if (this.index < this.data.length) {
// Push the next data chunk
this.push(this.data[this.index]);
this.index++;
} else {
// Signal end of stream
this.push(null);
}
}
}
// Usage
const stream = new MyReadable(['chunk1', 'chunk2', 'chunk3']);
stream.on('data', (chunk) => console.log(chunk));The _read() method must call this.push() to add data to the stream, and this.push(null) to signal the end.
Ensure the method is named exactly _read (with underscore prefix) and is defined as a class method:
// ✅ Correct - class method
class MyReadable extends Readable {
_read() {
// implementation
}
}
// ❌ Incorrect - arrow function property
class MyReadable extends Readable {
_read = () => {
// this won't work properly
}
}
// ❌ Incorrect - wrong name
class MyReadable extends Readable {
read() { // Missing underscore
// implementation
}
}If you have a custom constructor, always call super() first:
class MyReadable extends Readable {
constructor(options) {
// ✅ Call super() first
super(options);
// Then initialize your properties
this.data = [];
this.position = 0;
}
_read() {
// implementation
}
}Failing to call super() can cause the Readable internals to not be properly initialized.
If you don't need complex stream logic, consider using the simpler Readable.from() factory method (Node.js 12.3+):
const { Readable } = require('stream');
// From an array
const stream = Readable.from(['data1', 'data2', 'data3']);
// From an async generator
async function* generate() {
yield 'chunk1';
yield 'chunk2';
yield 'chunk3';
}
const stream2 = Readable.from(generate());This approach automatically handles the _read() implementation for you.
For production streams, handle the case when push() returns false (buffer is full):
class MyReadable extends Readable {
constructor(source) {
super();
this.source = source;
}
_read(size) {
const chunk = this.source.getNextChunk();
if (!chunk) {
// No more data
this.push(null);
return;
}
// push() returns false when buffer is full
const shouldContinue = this.push(chunk);
if (!shouldContinue) {
// Backpressure - wait for next _read() call
return;
}
}
}The _read() method will be called again when the stream is ready for more data.
Stream Implementation Patterns:
There are several ways to create Readable streams in Node.js, each with different trade-offs:
1. Class Extension (Traditional): Extending stream.Readable gives you full control but requires implementing _read(). This is best for complex stream logic with state management.
2. Simplified Construction: You can pass a read option to the Readable constructor:
const { Readable } = require('stream');
const stream = new Readable({
read() {
this.push('data');
this.push(null);
}
});3. Readable.from(): The modern approach for converting iterables or async iterables to streams. No _read() needed.
Object Mode Streams:
For streams that emit objects instead of strings/buffers, enable object mode:
class MyObjectStream extends Readable {
constructor() {
super({ objectMode: true });
}
_read() {
this.push({ id: 1, name: 'Item' });
this.push(null);
}
}Error Handling:
Always handle errors in your stream implementation:
_read() {
try {
const data = this.getNextData();
this.push(data || null);
} catch (err) {
this.destroy(err); // Properly destroy stream on error
}
}Performance Considerations:
The size parameter passed to _read(size) is advisory and indicates how much data the consumer wants. You can use this to optimize chunk sizes, but you're not required to push exactly that amount.
Error: Listener already called (once event already fired)
EventEmitter listener already called with once()
Error: EACCES: permission denied, open '/root/file.txt'
EACCES: permission denied
Error: Invalid encoding specified (stream encoding not supported)
How to fix Invalid encoding error in Node.js readable streams
Error: EINVAL: invalid argument, open
EINVAL: invalid argument, open
TypeError: readableLength must be a positive integer (stream config)
TypeError: readableLength must be a positive integer in Node.js streams