From b13a8c343f0d05caab019c4a6e4acbaca6e862d9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 6 Aug 2026 23:54:00 +0000 Subject: [PATCH] Fix crash loop: handle HTTP parse errors from keep-alive monitoring agents Monitored services that send data after Connection: close (malformed HTTP) were causing unhandled 'Parse Error' exceptions, crashing the process and triggering a PM2 restart loop (191 restarts observed) which caused 502s. Two-layer fix: - monitorService.js: attach .on('error', noop) to the shared HTTP/HTTPS keep-alive agents so socket-level parse errors don't escape as uncaught exceptions - index.js: process-level uncaughtException guard that swallows HTTP parse errors (HPE_INVALID_CONSTANT / 'Parse Error') while still crashing on real fatal errors Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_013AQYLFrscmMnzMiXAAANoa --- server/index.js | 11 +++++++++++ server/monitorService.js | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 2cf383e..bf0e504 100644 --- a/server/index.js +++ b/server/index.js @@ -6,6 +6,17 @@ const http = require('http'); const { randomUUID } = require('crypto'); require('dotenv').config(); +// Prevent HTTP parser errors (e.g. from keep-alive connections to monitored +// services that send data after Connection: close) from crashing the process. +process.on('uncaughtException', (err) => { + if (err.code === 'HPE_INVALID_CONSTANT' || (err.message && err.message.includes('Parse Error'))) { + console.error('HTTP parse error (swallowed):', err.message); + return; + } + console.error('Uncaught exception:', err); + process.exit(1); +}); + const db = require('./database'); const scheduler = require('./scheduler'); const monitorService = require('./monitorService'); diff --git a/server/monitorService.js b/server/monitorService.js index e353c59..ec044a5 100644 --- a/server/monitorService.js +++ b/server/monitorService.js @@ -31,9 +31,14 @@ function parseHostPort(urlString, fallbackPort) { class MonitorService { constructor() { - // Keep-alive agents for pooled HTTP/S connections + // Keep-alive agents for pooled HTTP/S connections. + // Socket-level 'error' listener prevents parse errors (e.g. "Data after + // Connection: close") from bubbling up as uncaught exceptions when a + // monitored server sends a malformed response on a reused connection. this.httpAgent = new http.Agent({ keepAlive: true, maxSockets: 50 }); + this.httpAgent.on('error', () => {}); this.httpsAgent = new https.Agent({ keepAlive: true, maxSockets: 50 }); + this.httpsAgent.on('error', () => {}); // Circuit breaker for HTTP checks (5 failures before opening, 60s reset) this.httpCircuitBreaker = new CircuitBreaker(5, 60000, this.isTransientError);