From 9f65041bda08adc01edc8836b8a63249dd053cd0 Mon Sep 17 00:00:00 2001 From: Scott Hawker Date: Thu, 2 Apr 2026 09:24:59 -0600 Subject: [PATCH 1/2] fix(stream): handle S3 upload errors without crashing the process When an S3 upload fails (e.g., RequestTimeout), the error flows through toS3()'s .catch() handler which calls toS3Stream.emit("error", err). If no error listener is attached to that stream, emit("error") throws, and since the throw happens inside a .catch() handler, it creates a new unhandled promise rejection that crashes the Lambda runtime. Three changes: 1. toS3() .catch(): Stop calling emit("error") on the stream. Just store the error and set the emittedError flag. The error propagates through the _final callback when the stream is ended. 2. leos3.js newStream(): Add an error listener on the internal s3 upload stream so any error events have a listener and don't throw. 3. leos3.js submitStream(): Listen for both "error" and "finish" events with a callback guard. Previously only "finish" was handled, but _final(callback) with a truthy error emits "error" not "finish". Task: ES-2976 Made-with: Cursor --- lib/stream/helper/leos3.js | 19 ++++++++++++++----- lib/stream/leo-stream.js | 5 +---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/stream/helper/leos3.js b/lib/stream/helper/leos3.js index cae5380..8304336 100644 --- a/lib/stream/helper/leos3.js +++ b/lib/stream/helper/leos3.js @@ -23,13 +23,19 @@ module.exports = function(ls, queue, configure, opts, onFlush) { function submitStream(callback) { if (count > 0) { - s3.on("finish", (err) => { - if (err) { - logger.error(err); - } else { + let called = false; + s3.on("error", (err) => { + if (!called) { + called = true; + callback(err); + } + }); + s3.on("finish", () => { + if (!called) { + called = true; logger.debug("UPLOADED FILE"); + callback(); } - callback(err); }); s3.end(); } else { @@ -72,6 +78,9 @@ module.exports = function(ls, queue, configure, opts, onFlush) { logger.info("S3 Location:", newFile); s3 = ls.toS3(configure.resources.LeoS3 || configure.s3, newFile); + s3.on('error', (err) => { + logger.error("S3 upload error for queue '" + queue + "':", err); + }); e = { event: queue, start: null, diff --git a/lib/stream/leo-stream.js b/lib/stream/leo-stream.js index d06e02d..591c4a8 100644 --- a/lib/stream/leo-stream.js +++ b/lib/stream/leo-stream.js @@ -174,10 +174,7 @@ module.exports = function(configure) { } }).done().catch(err => { uploadDonePromiseError = err; - if (toS3Stream) { - emittedError = true; - toS3Stream.emit("error", uploadDonePromiseError); - } + emittedError = true; }); toS3Stream = write((s, enc, done) => { From 9f45a09b60c208324b3415ea4c0ffac38d3754e6 Mon Sep 17 00:00:00 2001 From: Scott Hawker Date: Thu, 2 Apr 2026 10:17:48 -0600 Subject: [PATCH 2/2] revert: restore toS3() error emission in leo-stream.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert the leo-stream.js change from 9f65041. The original behavior of emitting the error on toS3Stream is correct — the fix in leos3.js adds the missing error listener that catches it. Task: ES-2976 --- lib/stream/leo-stream.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/stream/leo-stream.js b/lib/stream/leo-stream.js index 591c4a8..d06e02d 100644 --- a/lib/stream/leo-stream.js +++ b/lib/stream/leo-stream.js @@ -174,7 +174,10 @@ module.exports = function(configure) { } }).done().catch(err => { uploadDonePromiseError = err; - emittedError = true; + if (toS3Stream) { + emittedError = true; + toS3Stream.emit("error", uploadDonePromiseError); + } }); toS3Stream = write((s, enc, done) => {