From a2801908e39bff289a73b3865627ef3deafa66df Mon Sep 17 00:00:00 2001 From: Nikita Levyankov Date: Mon, 16 Feb 2026 10:29:51 +0200 Subject: [PATCH] fix(utils): prevent ERR_STREAM_WRITE_AFTER_END crash during log rotation Fixes race condition in date-based log rotation that caused crashes when Claude sessions crossed midnight. The async rotation fired but writeToLogFile continued to write to the stream being closed, causing stream write-after-end errors. Changes: - Add defensive checks in writeToLogFile() to skip writes during rotation - Check isRotating flag and stream.writable property before writing - Enhance close() method to wait for in-progress rotation (max 5 seconds) - Add detailed comments explaining the race condition scenario Impact: Prevents application crashes in long-running sessions that span midnight. Trade-off: May skip 1-2 log entries during rotation (acceptable vs crash). Generated with AI Co-Authored-By: codemie-ai --- src/utils/logger.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/utils/logger.ts b/src/utils/logger.ts index fcedcf9..dc25d4f 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -180,6 +180,14 @@ class Logger { }); } + // Defensive check: Skip write if rotation is in progress or stream is not writable + // This prevents ERR_STREAM_WRITE_AFTER_END race condition when log rotation occurs + // Race scenario: rotateLogFileIfNeeded() closes stream while writeToLogFile() tries to write + if (this.isRotating || !this.writeStream.writable) { + // Skip this write - next log call will use the new rotated stream + return; + } + try { const timestamp = new Date().toISOString(); @@ -210,8 +218,17 @@ class Logger { /** * Flush and close the write stream * Returns a Promise that resolves when all data is flushed + * Waits for any in-progress rotation to complete before closing */ - close(): Promise { + async close(): Promise { + // Wait for any in-progress rotation to complete + // This prevents race conditions where close() is called during rotation + const maxWaitTime = 5000; // 5 seconds max wait + const startTime = Date.now(); + while (this.isRotating && (Date.now() - startTime) < maxWaitTime) { + await new Promise(resolve => setTimeout(resolve, 100)); + } + return new Promise((resolve) => { if (this.writeStream) { this.writeStream.end(() => {