writable.end()
Signal that no more data will be written, with an optional final chunk.
writable.end([chunk[, encoding]][, callback])- Added in Node.js v0.9.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$chunkoptional | string | Buffer | TypedArray | DataView | any | Optional data to write. For streams not operating in object mode, chunk must be a string, Buffer, TypedArray or DataView. For object mode streams, chunk may be any JavaScript value other than null. |
$encodingoptional | string | The encoding if chunk is a string. |
$callbackoptional | Function | Callback for when the stream is finished. |
Return value
thisThe writable stream itself, allowing calls to be chained.
Errors
Calling write() after end() raises ERR_STREAM_WRITE_AFTER_END, and calling end() a second time raises ERR_STREAM_ALREADY_FINISHED.
Changelog
- PHP v22.0.0The chunk argument can now be a TypedArray or DataView instance (also in v20.13.0).
- PHP v15.0.0The callback is now invoked before 'finish' or on error, so code that assumed 'finish' ran first sees the opposite order.
- PHP v14.0.0The callback is invoked if 'finish' or 'error' is emitted.
- PHP v10.0.0This method now returns a reference to writable instead of undefined.
- PHP v8.0.0The chunk argument can now be a Uint8Array instance.
end() tells a writable stream that nothing further will be written. The stream flushes whatever is buffered, runs _final() if the implementation provides one, and then emits 'finish'. The optional chunk is written immediately before closing, which is why res.end('done') and file.end(lastLine) are so common.
When to reach for it
Any time you drive a writable by hand. A file stream that is never ended leaves buffered data unflushed and the descriptor open; an HTTP response that is never ended leaves the client waiting until it times out. If you used pipeline() or pipe(), the destination is ended for you and calling end() yourself is a bug.
What trips people up
end() is not synchronous. It schedules the shutdown; the data is not durable and the file is not closed when the call returns. Read the file on the next line and you may get nothing. Wait for the callback, the 'finish' event, or stream.finished().
Since Node 15 the end() callback fires before 'finish', reversing the order older code relied on. And 'finish' means the stream accepted everything, not that 'close' has fired — if you need the file descriptor released, listen for 'close' or use finished().
Example
When to use it
- Close a log file after the last line so its contents are flushed to disk
- Terminate an HTTP response with a final body chunk
- Signal end-of-input to a compressor so it can emit its trailer
- Release a file descriptor in a script that writes a report and exits
Frequently asked questions
How do I close a writable stream in Node.js?
Call writable.end(), optionally passing a final chunk and a callback; the stream flushes its buffer, runs _final(), then emits 'finish'. Do not call it on a destination that pipe() or pipeline() is managing, since they end it for you.
Why is my file empty right after calling end()?
Because end() is asynchronous — it schedules the flush rather than performing it, so a read on the next line races the write. Wait for the end() callback, the 'finish' event, or await stream.finished(writable).
What is the difference between the finish and close events?
'finish' fires when end() has been called and all data has been handed to the underlying system; 'close' fires later, once the stream and any file descriptor are actually released. Listen for 'close' when you need the resource freed.
More examples
Awaiting the close of a file you just wrote
The commented line is the classic bug: end() only schedules the flush, so a synchronous read straight afterwards races the write and often prints an empty string. Awaiting finished() resolves once 'finish' and 'close' have both happened, so the read is guaranteed to see "line one\nline two\n".
import fs from 'node:fs';
import { finished } from 'node:stream/promises';
const out = fs.createWriteStream('report.txt');
out.write('line one\n');
out.end('line two\n');
// WRONG: end() has not flushed yet, this may read an empty file.
// console.log(fs.readFileSync('report.txt', 'utf8'));
// Right: wait for the stream to be fully finished and closed.
await finished(out);
console.log(fs.readFileSync('report.txt', 'utf8'));
fs.rmSync('report.txt');Why you should not call end() inside a pipeline
pipeline() and pipe() both end the destination when the source finishes, so a manual end() afterwards raises ERR_STREAM_ALREADY_FINISHED. The commented option at the bottom is the supported way to keep a destination open across several pipelines — it is available on the promise form of pipeline().
import { pipeline } from 'node:stream/promises';
import { Readable } from 'node:stream';
import fs from 'node:fs';
const dest = fs.createWriteStream('out.txt');
await pipeline(Readable.from(['a', 'b'], { objectMode: false }), dest);
// pipeline() already ended the destination.
console.log('ended by pipeline:', dest.writableEnded); // true
try {
dest.end(); // ERR_STREAM_ALREADY_FINISHED
} catch (err) {
console.log('second end:', err.code);
}
// To keep the destination open for more writes, opt out explicitly:
// await pipeline(source, dest, { end: false });
Discussion