diff --git a/.changeset/smooth-wasps-hunt.md b/.changeset/smooth-wasps-hunt.md new file mode 100644 index 000000000..68a775ec4 --- /dev/null +++ b/.changeset/smooth-wasps-hunt.md @@ -0,0 +1,5 @@ +--- +"@react-email/render": patch +--- + +fix pipeable stream errors not being handled diff --git a/packages/render/src/node/read-stream.spec.ts b/packages/render/src/node/read-stream.spec.ts index a61867886..c2ec3adec 100644 --- a/packages/render/src/node/read-stream.spec.ts +++ b/packages/render/src/node/read-stream.spec.ts @@ -156,6 +156,31 @@ describe('readStream', () => { }); }); + describe('PipeableStream error handling', () => { + it('should reject when the source PipeableStream errors', async () => { + let hasError = false; + const erroringStream = new Readable({ + read() { + if (!hasError) { + this.push(Buffer.from('')); + hasError = true; + setImmediate(() => { + this.destroy(new Error('Streaming failed')); + }); + } + }, + }); + + const pipeableStream = { + pipe: (writable: any) => erroringStream.pipe(writable), + }; + + await expect(readStream(pipeableStream as any)).rejects.toThrow( + 'Streaming failed', + ); + }); + }); + describe('ReadableStream (pipeTo) path', () => { it('handles multi-byte characters with ReadableStream', async () => { const japaneseText = 'バクラクのメールテンプレートでスケジュール確認'; diff --git a/packages/render/src/node/read-stream.ts b/packages/render/src/node/read-stream.ts index e4226b654..d58e59b8e 100644 --- a/packages/render/src/node/read-stream.ts +++ b/packages/render/src/node/read-stream.ts @@ -39,13 +39,18 @@ export const readStream = async ( callback(); }, }); - stream.pipe(writable); - await new Promise((resolve, reject) => { + writable.on('pipe', (source) => { + source.on('error', (err: Error) => { + writable.destroy(err); + }); + }); writable.on('error', reject); writable.on('close', () => { resolve(); }); + + stream.pipe(writable); }); }