Skip to content

Commit e2a951d

Browse files
committed
fs: handle already errored writeFile streams
Keep the temporary writeFile error listener through the pending next-tick error emission when a stream is already errored at entry. This lets writeFile reject from the stored stream error without letting the stream emit an unhandled error, and still removes the listener after the pending emission. Signed-off-by: cookesan <6601329+cookesan@users.noreply.github.com>
1 parent 0c6ac44 commit e2a951d

3 files changed

Lines changed: 19 additions & 2 deletions

File tree

lib/internal/fs/promises.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ function makeWriteFileStreamErrorHandler(data) {
11431143
errored = true;
11441144
}
11451145
const streamError = isReadableErrored(data);
1146+
const wasErrored = streamError != null;
11461147
if (streamError != null)
11471148
onError(streamError);
11481149
data.on('error', onError);
@@ -1154,7 +1155,11 @@ function makeWriteFileStreamErrorHandler(data) {
11541155
throw error;
11551156
},
11561157
cleanup() {
1157-
data.removeListener('error', onError);
1158+
if (wasErrored) {
1159+
process.nextTick(() => data.removeListener('error', onError));
1160+
} else {
1161+
data.removeListener('error', onError);
1162+
}
11581163
},
11591164
};
11601165
}

test/parallel/test-fs-promises-file-handle-writeFile.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ function createErroredStream(error) {
100100
return stream;
101101
}
102102

103+
function waitForNextTick() {
104+
return new Promise((resolve) => process.nextTick(resolve));
105+
}
106+
103107
const bufferIterable = {
104108
expected: 'abc',
105109
*[Symbol.iterator]() {
@@ -142,7 +146,9 @@ async function doWriteStreamError() {
142146
fileHandle.writeFile(stream),
143147
{ message: error.message }
144148
);
145-
assert.strictEqual(stream.listenerCount('error'), 0);
149+
// FileHandle.writeFile() starts iteration before the next-tick error,
150+
// so the stream async iterator retains its own error listener.
151+
assert.strictEqual(stream.listenerCount('error'), 1);
146152
} finally {
147153
process.removeListener('uncaughtException', uncaughtException);
148154
await fileHandle.close();
@@ -162,6 +168,7 @@ async function doWriteAlreadyErroredStream() {
162168
fileHandle.writeFile(stream),
163169
{ message: error.message }
164170
);
171+
await waitForNextTick();
165172
assert.strictEqual(stream.listenerCount('error'), 0);
166173
} finally {
167174
process.removeListener('uncaughtException', uncaughtException);

test/parallel/test-fs-promises-writefile.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ function createErroredStream(error) {
6767
return stream;
6868
}
6969

70+
function waitForNextTick() {
71+
return new Promise((resolve) => process.nextTick(resolve));
72+
}
73+
7074
const bufferIterable = {
7175
expected: 'abc',
7276
*[Symbol.iterator]() {
@@ -135,6 +139,7 @@ async function doWriteAlreadyErroredStream() {
135139
fsPromises.writeFile(errorDest, stream),
136140
{ message: error.message }
137141
);
142+
await waitForNextTick();
138143
assert.strictEqual(stream.listenerCount('error'), 0);
139144
} finally {
140145
process.removeListener('uncaughtException', uncaughtException);

0 commit comments

Comments
 (0)