# Self-Referential .mcpb-cache Symlink Bug and bwrap Handling in Claude Desktop Debian

> Fix the self-referential .mcpb-cache symlink bug in Claude Desktop Debian. Learn how Bubblewrap handles it by detecting and removing the symlink before sandbox bind-mounts.

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

---

**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`](https://github.com/aaddrick/claude-desktop-debian/blob/main/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:

1. **`fs.lstatSync(hostPath)`** reads the inode metadata without dereferencing the symlink, allowing `isSymbolicLink()` to return true.
2. **`fs.readlinkSync(hostPath)`** retrieves the raw target string stored in the symlink.
3. **`path.resolve`** normalizes the target relative to the parent directory. If the resolved path equals the original `hostPath`, 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:

```javascript
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`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) (lines 1237–1250) demonstrates the complete fix:

```javascript
// 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-extra` library can leave a self-referential `.mcpb-cache` symlink after repeated Cowork sessions, causing **ELOOP** errors during bwrap mount operations.
- **Detection:** The fix in [`scripts/cowork-vm-service.js`](https://github.com/aaddrick/claude-desktop-debian/blob/main/scripts/cowork-vm-service.js) uses `fs.lstatSync` and `fs.readlinkSync` to identify symlinks that resolve to their own path.
- **Remediation:** `fs.unlinkSync` removes the broken symlink before `fs.mkdirSync` creates a valid directory, ensuring successful bind-mounting into the sandbox.
- **Location:** The logic resides in the `BwrapBackend.spawn` method (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`](https://github.com/aaddrick/claude-desktop-debian/blob/main/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`](https://github.com/aaddrick/claude-desktop-debian/blob/main/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.