How DesktopCommanderMCP Handles Uncaught Exceptions and Unhandled Rejections
DesktopCommanderMCP registers global process listeners in src/index.ts that log JSON parsing errors as warnings while capturing telemetry and terminating the process for all other fatal errors.
The DesktopCommanderMCP server implements a robust global error handling strategy to prevent silent failures and ensure observability. By attaching listeners to Node.js process events at startup, the server distinguishes between recoverable JSON parsing errors and critical failures that require immediate termination.
Global Error Handler Registration
When the server initializes, src/index.ts immediately registers two process-level listeners before starting the MCP server. This fail-fast approach ensures that no unexpected error leaves the server running in a corrupted state.
The uncaughtException Handler
The handler for synchronous exceptions extracts readable messages from thrown objects and applies special filtering for JSON errors. All other exceptions trigger telemetry capture and process termination.
process.on('uncaughtException', async (error) => {
const errorMessage = error instanceof Error ? error.message : String(error);
// JSON parsing errors are logged but do **not** terminate the process
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
logger.error(`JSON parsing error: ${errorMessage}`);
return;
}
capture('run_server_uncaught_exception', { error: errorMessage });
logger.error(`Uncaught exception: ${errorMessage}`);
process.exit(1);
});
The unhandledRejection Handler
Promise rejections receive identical treatment through a separate listener that mirrors the exception handling logic. This prevents unhandled async errors from leaving the server in an undefined state.
process.on('unhandledRejection', async (reason) => {
const errorMessage = reason instanceof Error ? reason.message : String(reason);
// JSON parsing errors are logged but **not** fatal
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
logger.error(`JSON parsing rejection: ${errorMessage}`);
return;
}
capture('run_server_unhandled_rejection', { error: errorMessage });
logger.error(`Unhandled rejection: ${errorMessage}`);
process.exit(1);
});
Special Handling for JSON Parsing Errors
The server treats JSON parsing errors as non-fatal warnings rather than crashes. When error messages contain both "JSON" and "Unexpected token", the handlers log the issue via logger.error() but return early, allowing the server to continue running with in-memory fallback configurations.
This design prevents service interruption when reading user-provided configuration files that may contain syntax errors.
Telemetry and Observability
Both handlers utilize the capture() utility from src/utils/capture.ts before termination. This function sends structured telemetry events to monitoring dashboards with specific event names:
run_server_uncaught_exceptionfor synchronous errorsrun_server_unhandled_rejectionfor promise rejections
The logger utility from src/utils/logger.ts writes human-readable messages to stderr/stdout, ensuring errors are visible both in logs and telemetry systems.
Fallback Protection in runServer()
As a final safety net, the top-level runServer() call wraps its entire initialization in a try…catch block. If any synchronous error escapes the global handlers, the catch block logs a FATAL ERROR, emits a structured error notification to the client, captures the failure telemetry, and exits the process.
Error Handling in Utility Scripts
The same robust pattern appears in auxiliary scripts:
uninstall-claude-server.jsmirrors the uncaught-exception logic for CLI operationssetup-claude-server.jsregisters identical handlers during server configuration
These scripts ensure consistent error observability across the entire DesktopCommanderMCP ecosystem.
Testing the Error Handlers
You can verify the server's error handling by simulating failures after startup:
// Simulates an uncaught exception
setTimeout(() => {
throw new Error('Simulated crash');
}, 1000);
// Simulates an unhandled promise rejection
Promise.reject(new Error('Simulated rejection'));
Running either snippet triggers the appropriate handler, produces a log entry, sends telemetry via capture(), and terminates the process with exit code 1 (unless the error matches the JSON-parsing pattern).
Summary
- Global listeners in
src/index.tsattach touncaughtExceptionandunhandledRejectionevents immediately upon startup - JSON parsing errors containing "Unexpected token" are logged but treated as non-fatal, keeping the server alive
- Fatal errors trigger telemetry capture via
capture(), error logging vialogger.error(), and immediate termination withprocess.exit(1) - Multiple safeguards include the global handlers and a top-level
try…catchinrunServer()to prevent silent failures - Consistent patterns appear across utility scripts like
setup-claude-server.jsanduninstall-claude-server.js
Frequently Asked Questions
Does DesktopCommanderMCP crash on all uncaught exceptions?
No. While the server terminates with exit code 1 for most uncaught exceptions and unhandled rejections, it specifically catches JSON parsing errors containing "Unexpected token" and treats them as warnings. These specific errors log a message via logger.error() but do not trigger process.exit(1), allowing the server to continue operating with fallback configurations.
What telemetry events does the server send when crashing?
According to the source code in src/utils/capture.ts, the server sends run_server_uncaught_exception for synchronous errors and run_server_unhandled_rejection for promise rejections. Both events include the error message string as metadata, enabling operators to diagnose root causes through monitoring dashboards.
Where are the global error handlers defined in DesktopCommanderMCP?
The primary handlers are defined in src/index.ts at the module level, executing immediately when the server starts before the runServer() function initializes. Identical logic also exists in uninstall-claude-server.js and setup-claude-server.js to ensure CLI utilities benefit from the same observability and fail-fast behavior.
What happens if an error escapes both global handlers?
The runServer() function wraps its entire initialization logic in a try…catch block. If an error somehow bypasses the process-level listeners, this final catch block logs a FATAL ERROR, notifies the MCP client with a structured error response, captures the failure via the capture() utility, and terminates the 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →