How Modly Handles Global Exception Handling and Logging in Electron

Modly centralizes all diagnostic output through a dedicated logger service in electron/main/logger.ts and registers global uncaughtException and unhandledRejection listeners in electron/main/index.ts to capture crashes, forward them to the renderer via IPC, and ensure no runtime failure goes unrecorded.

Modly, an open-source Electron application, implements a robust error management strategy that ensures every runtime failure is captured, logged, and communicated to the user interface. By funneling all diagnostic output through a single logger service and wiring global exception handlers to the renderer process, the application maintains a reliable audit trail across both main and renderer contexts. This article examines the specific implementation details of Modly's global exception handling and logging architecture as found in the lightningpixel/modly repository.

Centralized Logger Architecture

All logging in Modly flows through a single export from electron/main/logger.ts. This module exposes a logger object with methods for every severity level, including a custom python method used specifically by the Python bridge.

The logger provides standardized methods such as info, warn, error, and python, ensuring consistent formatting and transport configuration across the entire codebase. Any module requiring diagnostics simply imports the singleton:

import { logger } from './logger';

logger.info(`Operation completed successfully`);
logger.error(`Failed to process request`);

Global Exception Handlers in the Main Process

To prevent silent crashes, Modly registers process-level listeners immediately upon startup in electron/main/index.ts. These handlers capture both synchronous throws and asynchronous rejections that escape normal try/catch blocks.

Trapping Uncaught Exceptions

The uncaughtException handler filters out benign EPIPE errors—which occur when the launching terminal closes—and logs everything else before notifying the UI:

// electron/main/index.ts
process.on('uncaughtException', (err) => {
  if ((err as NodeJS.ErrnoException).code === 'EPIPE') return;
  logger.error(`Uncaught exception: ${err.stack ?? err.message}`);
  mainWindow?.webContents.send('app:error', err.stack ?? err.message);
});

This design prevents pipe-related noise from polluting logs while ensuring critical stack traces reach both the filesystem and the user interface.

Capturing Unhandled Promise Rejections

Asynchronous failures are handled via the unhandledRejection event. The handler converts the rejection reason to a string, logs it through the central logger, and forwards it to the renderer using the same IPC channel:

// electron/main/index.ts
process.on('unhandledRejection', (reason) => {
  const msg = String(reason);
  logger.error(`Unhandled rejection: ${msg}`);
  mainWindow?.webContents.send('app:error', msg);
});

Bidirectional Error Communication

Modly establishes a two-way error reporting pipeline between the main and renderer processes, ensuring developers receive complete context regardless of which process originates the failure.

Forwarding Main Process Errors to the Renderer

When the global handlers catch an exception, they transmit the error message to the renderer via the app:error IPC channel. This allows the frontend to display user-friendly error notifications while the main process handles the technical logging:

mainWindow?.webContents.send('app:error', err.stack ?? err.message);

Ingesting Renderer-Side Errors

The main process also listens for explicit error reports from the renderer. In electron/main/ipc-handlers.ts, Modly registers a listener on the log:error channel that prefixes the message with its origin and routes it through the central logger:

// electron/main/ipc-handlers.ts
ipcMain.on('log:error', (_event, message: string) => 
  logger.error(`[Renderer] ${message}`)
);

Renderer code can trigger this pipeline when catching local exceptions:

// In renderer process
window.electron.ipcRenderer.send('log:error', errorMessage);

Specialized Logging for the Python Bridge

Modules interacting with external processes utilize the logger's custom methods. In electron/main/python-bridge.ts, stdout and stderr from the Python subprocess are routed through logger.python(), keeping native process output in the same log stream as Electron's internal diagnostics:

// electron/main/python-bridge.ts
logger.python(msg);               // regular output
logger.python(`[stderr] ${msg}`); // error output

Application Lifecycle Logging

When the application finishes initializing, Modly records a startup marker that includes the current version, providing a clear boundary in log files for debugging session-related issues:

// electron/main/index.ts
logger.info(`App started — version ${app.getVersion()}`);

Summary

  • Centralized Service: All logging flows through electron/main/logger.ts, providing consistent formatting and transport logic.
  • Global Handlers: The main process registers uncaughtException and unhandledRejection listeners in electron/main/index.ts to capture fatal errors.
  • Noise Filtering: The exception handler explicitly ignores EPIPE errors to prevent terminal-disconnect crashes.
  • UI Integration: Errors are forwarded to the renderer via the app:error IPC channel, enabling real-time user feedback.
  • Bidirectional Flow: Renderer errors flow back to the main logger through the log:error IPC channel handled in electron/main/ipc-handlers.ts.
  • Python Integration: The logger.python() method in electron/main/python-bridge.ts consolidates subprocess output with application logs.

Frequently Asked Questions

How does Modly prevent EPIPE errors from crashing the application?

In electron/main/index.ts, the uncaughtException handler checks the error code and immediately returns if it equals 'EPIPE'. This prevents the application from terminating when the parent terminal closes, while still logging genuine runtime exceptions.

What IPC channels does Modly use for error reporting?

Modly uses two primary channels: app:error for main-to-renderer communication (sending crashes to the UI), and log:error for renderer-to-main communication (forwarding frontend errors to the central log file).

Can Modly's logger handle output from Python subprocesses?

Yes. The electron/main/python-bridge.ts module uses a custom logger.python() method exposed by electron/main/logger.ts to route both stdout and stderr from the Python interpreter into the same consolidated log pipeline used by the Electron main process.

Where are global exception handlers registered in Modly's codebase?

The global process.on('uncaughtException') and process.on('unhandledRejection') listeners are registered in electron/main/index.ts, ensuring they are active immediately upon application startup and remain active for the entire lifecycle of the main process.

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 →