# How DesktopCommanderMCP's Config Manager Write Chain Prevents Configuration Corruption

> Discover how DesktopCommanderMCP's ConfigManager writeChain serialization prevents configuration corruption by blocking partial writes and ensuring sequential disk operations.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: internals
- Published: 2026-08-04

---

**The writeChain in DesktopCommanderMCP's ConfigManager prevents corruption by serializing all disk writes through a single promise chain, ensuring no two writes run concurrently and blocking partial writes from reaching the filesystem.**

The DesktopCommanderMCP repository implements a robust configuration persistence layer that handles concurrent updates, partial write failures, and high-frequency changes without corrupting [`config.json`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/config.json). The `ConfigManager` class in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) achieves this through a carefully designed `writeChain` mechanism that orders every file operation sequentially.

## Single Promise Chain Eliminates Race Conditions

The foundation of corruption prevention starts at initialization. In [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) at line 56, the constructor creates `writeChain` as a resolved promise:

```typescript
this.writeChain = Promise.resolve();

```

Every subsequent write appends to this chain using `.then()`. Because JavaScript promises settle exactly once, each `writeConfigToDisk` call must complete before the next begins. Simultaneous invocations of `setValue()`, `saveConfig()`, or `scheduleSave()` cannot interleave their filesystem operations— they queue automatically through the chain.

## Atomic File Writes via writeConfigToDisk

The actual persistence logic resides in `writeConfigToDisk` at line 197:

```typescript
private async writeConfigToDisk(): Promise<void> {
    await fs.writeFile(
        this.configPath,
        JSON.stringify(this.config, null, 2),
        'utf-8'
    );
}

```

This performs a single `fs.writeFile` call with the fully stringified configuration object. The operation is atomic at the system call level: the file either contains the complete new JSON blob or the previous valid state. No partially written data ever appears on disk.

## Error-Resilient Chain Continuation

Failed writes cannot break the serialization guarantee. After scheduling each write, the manager updates `writeChain` and attaches an empty `.catch()` handler at line 208:

```typescript
this.writeChain = this.writeChain.then(() => this.writeConfigToDisk()).catch(() => {});

```

A rejected promise would normally halt subsequent `.then()` handlers. The `.catch(() => {})` swallows errors, allowing the chain to continue. Later writes proceed regardless of earlier failures, maintaining ordering guarantees even when disk space is exhausted or permissions are denied.

## Coalesced Background Saves for High-Frequency Updates

Telemetry counters and similar non-critical updates use `scheduleSave()` to avoid disk thrashing. This method implements two protective mechanisms in [`src/config-manager.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/config-manager.ts) starting at line 220:

- **`saveScheduled` flag guard** — If a background write is already queued, subsequent calls return immediately without creating new chain entries.

- **Single merged write** — When the delayed write executes at line 226, it resets `saveScheduled` and calls `writeConfigToDisk` once. All in-memory changes accumulated during the delay persist together.

This collapsing behavior prevents a rapid stream of updates from generating hundreds of individual file writes, reducing wear on SSDs and eliminating windows where crashes could leave partially updated state.

## Explicit Blocking Saves for Critical Operations

User-initiated actions requiring guaranteed persistence call `saveConfig()` at line 205:

```typescript
public async saveConfig(): Promise<void> {
    await this.writeChain;
}

```

Like background saves, this appends to `writeChain`. The returned promise resolves only after all prior writes complete, ensuring that even if `scheduleSave()` queued a write milliseconds earlier, the explicit save waits its turn and observes the fully updated state.

## Practical Usage Examples

```typescript
import { configManager } from './config-manager.js';

// Critical UI action: blocks until written
await configManager.setValue('defaultShell', '/bin/bash');

// Telemetry update: coalesced in background
await configManager.setValueNonBlocking('commandCount', 42);

// Bulk update: only one disk write despite 100 calls
for (let i = 0; i < 100; i++) {
    configManager.setValueNonBlocking(`key${i}`, i);
}

```

## Summary

- **Serial execution**: The `writeChain` promise queue guarantees one filesystem operation at a time, preventing interleaved writes that could corrupt JSON structure.

- **Atomic writes**: `writeConfigToDisk` writes complete stringified state in a single system call via `fs.writeFile`.

- **Fault tolerance**: `.catch(() => {})` handlers ensure chain continuity through disk errors without dropping later updates.

- **Write coalescing**: `scheduleSave()` collapses rapid updates into single operations via the `saveScheduled` flag, reducing I/O load and crash vulnerability.

- **Explicit durability**: `saveConfig()` provides blocking persistence for operations requiring immediate, confirmed storage.

## Frequently Asked Questions

### What happens if two setValue() calls run simultaneously?

Both calls append their writes to `writeChain` in the order received. The first queued `writeConfigToDisk` executes completely before the second begins. The later call reads the updated in-memory state, so its write includes all prior changes.

### Does writeChain prevent data loss if the process crashes?

`writeChain` prevents file corruption, not data loss. Unsaved in-memory state disappears on crash. For durability, use `await saveConfig()` or `setValue()` (which saves) before critical operations. Background-scheduled saves may lose their delay window.

### Why use .catch(() => {}) instead of proper error handling?

The empty catch keeps the chain alive for subsequent writes. Individual write failures are logged separately in `writeConfigToDisk`. Without this handler, a single disk error would permanently stall all future configuration updates.

### How does scheduleSave() differ from direct saveConfig() calls?

`scheduleSave()` is fire-and-forget: it updates memory immediately but delays disk I/O, coalescing multiple calls. `saveConfig()` and `setValue()` wait for the chain to settle, returning only after the file reflects current state.