# Daemon Auto-Spawn Mechanism for Cowork Sessions in Claude Desktop

> Automatically relaunch the cowork vm service with the daemon auto-spawn mechanism. This feature intercepts ECONNREFUSED errors and forks a new process. Ensure seamless Cowork sessions in Claude Desktop.

- Repository: [Aaddrick/claude-desktop-debian](https://github.com/aaddrick/claude-desktop-debian)
- Tags: internals
- Published: 2026-04-19

---

**The daemon auto-spawn mechanism automatically relaunches the cowork-vm-service within 10 seconds of a crash by intercepting ECONNREFUSED errors and forking a new process before the client retry loop.**

The **daemon auto-spawn mechanism for Cowork sessions** solves a critical reliability issue in the community-maintained `aaddrick/claude-desktop-debian` repository. When Claude Desktop runs in Cowork mode on Linux, it depends on a Node.js daemon to serve the VM bundle over a Unix-domain socket. The original implementation only retried connections when the socket was missing entirely, leaving users stranded when the daemon crashed mid-session.

## The Problem: Daemon Crashes Left Users Stuck

The Linux Cowork client originally checked for `ENOENT` (socket not found) to trigger reconnection attempts. However, if the **cowork-vm-service** daemon crashed while the socket file persisted, the client received `ECONNREFUSED` and immediately gave up. This created a broken state where the socket existed but no process was listening, forcing users to manually restart Claude Desktop or kill the stale socket.

## How the Auto-Spawn Mechanism Works

Patch 6 in [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh) implements a surgical fix that expands error handling and injects auto-launch logic directly into the minified Electron bundle. The mechanism operates in three phases:

### Expanding Error Detection

The patch locates the error check at lines 1329-1355 in [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh) and rewrites the condition to treat `ECONNREFUSED` on Linux as equivalent to `ENOENT`. This ensures the client enters the retry loop regardless of whether the socket is missing or the daemon has crashed.

### Rate-Limited Daemon Forking

Before the retry delay executes, the patch inserts a guard at lines 1395-1419 that prevents fork bombs. The logic checks a `_lastSpawn` timestamp attached to the retry function:

- If unset or greater than 10,000 ms ago, it stores `Date.now()` and forks the daemon
- If within the 10-second window, it skips spawning and allows the existing retry delay to continue

This **10-second cooldown** balances rapid recovery with system safety.

### Persistent Logging Implementation

The forked daemon at lines 1420-1445 uses `child_process.fork()` with detached mode and pipes `stdout` and `stderr` to `~/.config/Claude/logs/cowork_vm_daemon.log`. The `logLifecycle()` function in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) (lines 49-86) writes structured events including PID, working directory, and signal handling, creating an audit trail for debugging crashes.

## Technical Implementation Details

The entire mechanism is implemented via build-time patching, requiring no modifications to the upstream Claude Desktop binary at runtime.

### Build-Time Patching Strategy

The [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh) script (Patch 6, lines 1450-1465) uses sed-like operations to locate specific patterns in the minified JavaScript, then splices the auto-spawn snippet directly before the `await new Promise(resolve => setTimeout(resolve, …))` retry delay. Patch 6b (lines 1630-1660) extends this by cleaning up stale VM images (`sessiondata.img` and `rootfs.img.zst`) that can block daemon startup during auto-reinstall scenarios.

### Daemon Lifecycle Management

When spawned, the daemon creates its Unix-domain socket at `$XDG_RUNTIME_DIR/cowork-vm-service.sock` and enters an event loop. The client’s retry loop (typically 2-second delays) allows the daemon sufficient time to initialize before the next connection attempt. If the daemon exits cleanly, it removes its socket; if it crashes, the socket persists but the next client attempt triggers the auto-spawn mechanism via the `ECONNREFUSED` handler.

## Practical Usage and Debugging

Monitor and interact with the auto-spawn mechanism using standard Linux tools.

### Checking Daemon Status

Verify the cowork-vm-service is running and identify its PID:

```bash
pgrep -af cowork-vm-service.js

```

Expected output shows the Node.js process and script path:

```

3124 /usr/bin/node /opt/Claude/app.asar.unpacked/cowork-vm-service.js

```

### Monitoring Logs

Follow daemon lifecycle events in real-time:

```bash
tail -f ~/.config/Claude/logs/cowork_vm_daemon.log

```

The `logLifecycle()` function generates structured entries:

```

lifecycle startup pid=3124 cwd=/home/user/.config/Claude
lifecycle listening on /run/user/1000/cowork-vm-service.sock
lifecycle SIGTERM received
lifecycle exit code=0

```

### Testing Respawn Behavior

Force a crash to verify the mechanism:

```bash

# Kill the daemon (simulates crash)

pkill -9 -f cowork-vm-service.js

# The next Cowork connection attempt triggers auto-spawn

# Check logs after ~2 seconds to confirm restart

```

Verify the 10-second cooldown by rapid cycling:

```bash
for i in {1..5}; do 
  pkill -9 -f cowork-vm-service.js
  sleep 1
done

```

Only the first kill triggers a new spawn; subsequent attempts within the window are ignored. Check the client log for `[cowork-autolaunch]` messages indicating cooldown skips.

## Summary

- **The daemon auto-spawn mechanism for Cowork sessions** intercepts `ECONNREFUSED` errors on Linux, treating them as restart triggers rather than fatal failures.
- **Patch 6** in [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh) injects a rate-limited fork guard that prevents the daemon from respawning more than once per 10 seconds.
- **Logs** persist to `~/.config/Claude/logs/cowork_vm_daemon.log` via the `logLifecycle()` function in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js).
- **No runtime binary modifications** are required; the fix operates entirely through build-time patching of the Electron bundle.

## Frequently Asked Questions

### What triggers the daemon auto-spawn mechanism?

The mechanism activates when the Claude Desktop Cowork client receives an `ECONNREFUSED` error while attempting to connect to `$XDG_RUNTIME_DIR/cowork-vm-service.sock`. Originally, the client only handled `ENOENT` (missing socket), but Patch 6 in [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh) extends the error check to treat `ECONNREFUSED` as a restart condition, indicating the daemon has crashed while the socket file persists.

### How does the 10-second cooldown prevent fork bombs?

The patch injects a `_lastSpawn` timestamp check on the retry function that stores `Date.now()` on each successful spawn. Before forking a new daemon process, the guard verifies that either `_lastSpawn` is undefined or the current time exceeds `_lastSpawn` by 10,000 milliseconds. This rate limit ensures that even if the daemon crashes immediately on startup, the system will not enter a runaway process creation loop.

### Where are the daemon logs stored?

The auto-spawn mechanism redirects the forked daemon's `stdout` and `stderr` to `~/.config/Claude/logs/cowork_vm_daemon.log`. Additionally, the daemon itself uses the `logLifecycle()` function defined in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) to write structured events including startup PID, listening socket path, and signal termination details, providing a complete audit trail for debugging crashes and respawn cycles.

### Does this mechanism require modifying the Claude Desktop binary?

No runtime modifications to the upstream Claude Desktop binary are necessary. The entire auto-spawn mechanism is implemented via **Patch 6** in [`build.sh`](https://github.com/aaddrick/claude-desktop-debian/blob/main/build.sh), which performs build-time patching of the minified JavaScript bundle. The script locates specific error handling patterns and the retry delay logic, then splices in the rate-limited fork guard before the `await new Promise(resolve => setTimeout(resolve, …))` statement, ensuring the fix persists across updates without touching the original Electron executable.