Daemon Auto-Spawn Mechanism for Cowork Sessions in Claude Desktop
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 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 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 (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 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:
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:
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:
# 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:
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
ECONNREFUSEDerrors on Linux, treating them as restart triggers rather than fatal failures. - Patch 6 in
build.shinjects 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.logvia thelogLifecycle()function inscripts/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 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 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, 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.
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 →