What Is the process-safety-net and How Does It Prevent Memory Leaks?

The process-safety-net is a global error handler that intercepts transient network-transport errors (like ECONNRESET from undici) to prevent unnecessary process crashes, thereby stopping the repeated resource allocation cycles that manifest as memory leaks in containerized deployments.

In the tashfeenahmed/freellmapi repository, the process-safety-net module serves as a critical resilience layer for the Node.js server. Without it, transient HTTP/2 socket resets and CDN connection drops trigger fatal process exits that, when combined with automatic restarts in Docker or systemd, create resource churn mistaken for memory leaks. This library swallows only specific transport errors while preserving fail-fast behavior for genuine application bugs.

Why Uncaught Transport Errors Simulate Memory Leaks

When Node.js encounters an uncaught exception or unhandled promise rejection, it terminates the process with exit code 1. In production environments using container orchestration, this triggers an immediate restart. Each crash and restart cycle abandons open sockets, active timers, and in-memory caches without proper cleanup, then immediately reallocates them upon boot. Over time, this pattern appears as a monotonic memory increase in monitoring dashboards, even though the underlying issue is merely a flaky network transport rather than a true memory leak.

How the process-safety-net Filters Errors

The implementation in server/src/lib/process-safety-net.ts uses a functional, allow-list approach to distinguish between fatal bugs and recoverable network blips.

Defining Transport Error Signatures

The library maintains two constant sets that define safe-to-ignore errors:

  • TRANSPORT_ERROR_CODES: Contains strings like ECONNRESET, UND_ERR_SOCKET, and other undici-specific error codes
  • TRANSPORT_MESSAGE_HINTS: Includes substrings like fetch failed and socket hang up that indicate transient HTTP client failures

These constants form the baseline for the classification logic.

Walking the Error Chain

Network errors in modern Node.js often nest causes. The walkErrorChain function recursively traverses the err.cause property of Error objects, collecting every code and message property into a flat array for inspection.

Classification Logic

The isTransportError function checks if any collected code or message matches the allow-lists. If a match exists, classifyProcessError returns the string 'swallow'; otherwise, it returns 'fatal'. This ensures that only genuine programming errors or system failures trigger process termination.

Global Handler Installation

The installProcessSafetyNet function registers two process-level listeners:

  1. process.on('uncaughtException', handler)
  2. process.on('unhandledRejection', handler)

Both delegate to handleProcessError, which logs the decision via a configurable logger and either returns quietly (for transport errors) or calls process.exit(1) (for fatal errors).

Boot Integration in freellmapi

In server/src/index.ts, the safety net installs as the first operation in the main() function:

import { installProcessSafetyNet } from './lib/process-safety-net.js';

async function main() {
  // Install before any network activity
  installProcessSafetyNet();
  
  // ... remainder of server startup
}

This early installation ensures that even errors thrown during the initial fetch() calls to upstream LLM APIs are caught and filtered appropriately.

Customizing Error Handling

The library accepts optional log and exit hooks for integration with observability platforms:

installProcessSafetyNet({
  log: (level, ...args) => {
    myLogger[level]('[safety-net]', ...args);
  },
  exit: (code) => {
    metrics.increment('process.fatal_exit', { code });
    process.exit(code);
  },
});

This pattern allows teams to emit structured logs or alert on fatal exits while maintaining silence during routine network hiccups.

Testing Transport Error Classification

You can verify the classification logic without terminating your process by importing handleProcessError directly:

import { handleProcessError } from './lib/process-safety-net.js';

// Simulate a CDN socket reset
const err = new Error('socket hang up');
(err as any).code = 'ECONNRESET';

const decision = handleProcessError('uncaughtException', err);
console.log(decision); // 'swallow' - process continues

This approach is used in the unit tests at server/src/__tests__/lib/process-safety-net.test.ts to confirm that transient errors return 'swallow' while genuine bugs return 'fatal'.

Summary

  • The process-safety-net prevents memory leaks by stopping crash-restart cycles caused by transient network errors like ECONNRESET.
  • It uses allow-lists (TRANSPORT_ERROR_CODES, TRANSPORT_MESSAGE_HINTS) to identify safe-to-ignore transport failures from undici and Node.js HTTP clients.
  • The walkErrorChain and isTransportError functions recursively inspect nested error causes to make classification decisions.
  • installProcessSafetyNet registers global handlers early in the boot sequence of tashfeenahmed/freellmapi to protect against fatal exits during flaky CDN connections.
  • Genuine bugs still trigger process.exit(1) via handleProcessError, preserving Node.js fail-fast semantics for non-network errors.

Frequently Asked Questions

What specific error codes does the process-safety-net catch?

The safety net specifically watches for transport-layer codes including ECONNRESET, EPIPE, UND_ERR_SOCKET, and UND_ERR_REQ_RETRY. It also scans error messages for phrases like socket hang up and fetch failed to identify transient HTTP/2 connection drops from the undici client.

Does swallowing errors hide memory leaks in my application code?

No. The process-safety-net only swallows errors matching the predefined transport error allow-lists. All other exceptions, including actual memory exhaustion (RangeError: Maximum call stack size exceeded) or unhandled rejections from application logic, are classified as fatal and trigger immediate process.exit(1). This preserves the fail-fast behavior for genuine bugs.

Where should I install the process-safety-net in my server?

Install the safety net as the very first operation in your entry point, before any import statements that initiate network connections or database pools. In freellmapi, this occurs in server/src/index.ts at the top of the main() function, ensuring protection covers the entire application lifecycle including initial upstream API handshakes.

Can I test the process-safety-net classification logic?

Yes. The module exports handleProcessError and isTransportError for unit testing. You can pass mock error objects to verify that your specific error conditions are classified correctly as either swallow or fatal without actually terminating the process, as demonstrated in the project's test suite at server/src/__tests__/lib/process-safety-net.test.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →