From f621659eca2d7b564d2b10139fbe8ded339db014 Mon Sep 17 00:00:00 2001 From: dinex-dev Date: Thu, 30 Jul 2026 23:07:08 +0530 Subject: [PATCH 1/2] fix(har): guard HAR logging when a request matches no mock [RQ-2398] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HAR middleware's `finish` listener read `res.locals.rq_metadata.mockId` unconditionally. For requests that don't match any mock, `rq_metadata` is undefined, so this threw a `TypeError` inside the `finish` event callback — outside Express's request-handling context, where it surfaces as an uncaught exception rather than a handled request error. - Read the id via optional chaining (`res.locals.rq_metadata?.mockId`). - Skip logging when there is no `mockId`; an unmatched request has no mock to attach the log to (and the downstream sink keys logs by mockId). - Wrap the callback in try/catch so a logging failure can never escape the finish handler. Co-Authored-By: Claude Opus 4.8 --- src/middlewares/har.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/middlewares/har.ts b/src/middlewares/har.ts index e4dd002..a45343d 100644 --- a/src/middlewares/har.ts +++ b/src/middlewares/har.ts @@ -18,14 +18,26 @@ export const HarMiddleware = (req: Request, res: Response, next: NextFunction) = }; res.once('finish', () => { - const HarEntry: Partial = { - time: Date.now() - requestStartTime.getTime(), - startedDateTime: requestStartTimeStamp, - request: buildHarRequest(req), - response: buildHarResponse(res, { body: responseBody }), - } + try { + const mockId = res.locals.rq_metadata?.mockId; + // No matching mock (e.g. 404) means there's nothing to attach the log to. + if (!mockId) { + return; + } + + const HarEntry: Partial = { + time: Date.now() - requestStartTime.getTime(), + startedDateTime: requestStartTimeStamp, + request: buildHarRequest(req), + response: buildHarResponse(res, { body: responseBody }), + } - storageService.storeLog({ mockId: res.locals.rq_metadata.mockId, HarEntry, }) + storageService.storeLog({ mockId, HarEntry, }) + } catch (error) { + // Never let a logging failure escape the finish handler — it would + // surface as an uncaught exception and can crash the process. + console.error("[HarMiddleware] Failed to store log", error); + } }); next(); From 6f3f3a0f6837d1e91443b84018820994b5166c4d Mon Sep 17 00:00:00 2001 From: dinex-dev Date: Thu, 30 Jul 2026 23:26:51 +0530 Subject: [PATCH 2/2] fix(har): await storeLog so async rejections are caught MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit storageService.storeLog returns a Promise, so the synchronous try/catch did not cover a rejection from the sink's sendLog — it would surface as an unhandled rejection. Make the finish callback async and await the call so the rejection lands in the existing catch. Co-Authored-By: Claude Opus 4.8 --- src/middlewares/har.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/middlewares/har.ts b/src/middlewares/har.ts index a45343d..dd0f93c 100644 --- a/src/middlewares/har.ts +++ b/src/middlewares/har.ts @@ -17,7 +17,7 @@ export const HarMiddleware = (req: Request, res: Response, next: NextFunction) = return originalSend.call(this, body); }; - res.once('finish', () => { + res.once('finish', async () => { try { const mockId = res.locals.rq_metadata?.mockId; // No matching mock (e.g. 404) means there's nothing to attach the log to. @@ -32,7 +32,9 @@ export const HarMiddleware = (req: Request, res: Response, next: NextFunction) = response: buildHarResponse(res, { body: responseBody }), } - storageService.storeLog({ mockId, HarEntry, }) + // Await so a rejected storeLog is caught here rather than surfacing + // as an unhandled rejection. + await storageService.storeLog({ mockId, HarEntry, }) } catch (error) { // Never let a logging failure escape the finish handler — it would // surface as an uncaught exception and can crash the process.