The previous fix (agent .on('error')) doesn't reach individual socket
objects — socket-level parse errors still escaped as uncaught exceptions.
Root fix: disable keepAlive on the HTTP/HTTPS monitoring agents. Without
connection reuse there is no stale connection to trigger "Parse Error:
Data after Connection: close". Monitoring intervals are 60s+ so the lack
of connection pooling has no meaningful performance impact.
Belt-and-suspenders: add process.on('unhandledRejection') alongside the
existing uncaughtException guard to catch any async paths that leak.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AQYLFrscmMnzMiXAAANoa
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AQYLFrscmMnzMiXAAANoa
package-lock.json was missing "hasInstallScript": true for better-sqlite3.
npm ci reads this flag to decide whether to run a package's install scripts;
without it, the native .node binding is never compiled and the server crashes
at startup with "Cannot find module '...better_sqlite3.node'".
Running npm install regenerates the field correctly. npm ci (used in
update-server.sh) can now compile the native module during deployment.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013AQYLFrscmMnzMiXAAANoa