Self-Referential .mcpb-cache Symlink Bug and bwrap Handling in Claude Desktop Debian
The Claude Desktop Debian package fixes an ELOOP error caused by a self-referential .mcpb-cache symlink by detecting and removing it before bind-mounting directories into the Bubblewrap sandbox.
Claude Desktop uses a Bubblewrap (bwrap) sandbox to isolate user code execution, but a regression in the upstream fs-extra library can create a broken symlink that prevents new sessions from starting. This article explains how the aaddrick/claude-desktop-debian repository detects and mitigates this issue within its Cowork VM service.
Understanding the Self-Referential Symlink Bug
During a Cowork session, the backend creates host-side directories (e.g., ~/.local/share/claude-desktop/vm/sessions/<name>/mnt/*) and bind-mounts them into the bwrap sandbox. The .mcpb-cache directory is intended to store cached data between sessions.
Due to a regression in fs-extra, repeated session cycles can cause .mcpb-cache to be created as a self-referential symlink—a link that points to itself. When the system attempts to mkdir or bind-mount this path, it encounters an ELOOP ("Too many symbolic links") error, aborting the VM launch.
How bwrap Handling Detects and Removes the Symlink
The fix is implemented in the BwrapBackend.spawn routine within scripts/cowork-vm-service.js. Before creating any mount point directories, the code checks for and sanitizes self-referential symlinks.
Detection Logic in cowork-vm-service.js
The detection sequence uses synchronous filesystem methods to examine the path without following the link:
fs.lstatSync(hostPath)reads the inode metadata without dereferencing the symlink, allowingisSymbolicLink()to return true.fs.readlinkSync(hostPath)retrieves the raw target string stored in the symlink.path.resolvenormalizes the target relative to the parent directory. If the resolved path equals the originalhostPath, the symlink is self-referential.
Safe Directory Creation
Once identified, the offending link is removed with fs.unlinkSync(hostPath), breaking the loop. The code then proceeds with standard directory creation:
fs.mkdirSync(hostPath, { recursive: true });
This ensures the bwrap sandbox receives a valid directory for bind-mounting, preventing the ELOOP error from propagating to the mount system call.
Code Implementation Details
The following excerpt from scripts/cowork-vm-service.js (lines 1237–1250) demonstrates the complete fix:
// Inside BwrapBackend.spawn (scripts/cowork-vm-service.js)
// -------------------------------------------------------
try {
// ---- Fix #342: upstream fs-extra can create .mcpb-cache
// as a self-referential symlink after repeated sessions.
// Detect and delete it before mkdir so the bind mount succeeds.
const st = fs.lstatSync(hostPath);
if (st.isSymbolicLink()) {
const target = fs.readlinkSync(hostPath);
const resolved = path.resolve(path.dirname(hostPath), target);
if (resolved === hostPath) {
log(`BwrapBackend spawn: removing self-referential symlink: ${hostPath}`);
fs.unlinkSync(hostPath); // ← break the loop
}
}
} catch {
// ENOENT is fine — path doesn’t exist yet
}
// Ensure the mount directory exists for the sandbox
if (!fs.existsSync(hostPath)) {
fs.mkdirSync(hostPath, { recursive: true });
}
The catch block silently ignores ENOENT errors, which occur when the path does not exist—a normal condition during the first session.
Summary
- Root Cause: The
fs-extralibrary can leave a self-referential.mcpb-cachesymlink after repeated Cowork sessions, causing ELOOP errors during bwrap mount operations. - Detection: The fix in
scripts/cowork-vm-service.jsusesfs.lstatSyncandfs.readlinkSyncto identify symlinks that resolve to their own path. - Remediation:
fs.unlinkSyncremoves the broken symlink beforefs.mkdirSynccreates a valid directory, ensuring successful bind-mounting into the sandbox. - Location: The logic resides in the
BwrapBackend.spawnmethod (lines 1237–1250) and is documented in the README (lines 226–228).
Frequently Asked Questions
What causes the ELOOP error in Claude Desktop Debian?
The ELOOP error occurs when the .mcpb-cache directory becomes a self-referential symlink due to a regression in the fs-extra library. When bwrap attempts to bind-mount this path, the kernel detects the circular reference and returns "Too many symbolic links," aborting the VM launch.
How does the bwrap backend detect self-referential symlinks?
The detection logic uses fs.lstatSync to read the symlink inode without following it, then calls fs.readlinkSync to retrieve the target string. By resolving the target with path.resolve and comparing it to the original path, the code identifies when a symlink points to itself.
Where is the symlink fix implemented in the codebase?
The fix resides in scripts/cowork-vm-service.js inside the BwrapBackend.spawn routine, specifically between lines 1237 and 1250. This is where the mount preparation logic checks for and removes self-referential symlinks before creating directories.
Can this bug affect other directories besides .mcpb-cache?
While the .mcpb-cache directory is the specific path affected by the fs-extra regression, the defensive code in cowork-vm-service.js performs the symlink check on any host path being prepared for bind-mounting. This ensures robustness against similar filesystem anomalies in other mount points.
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 →