claude-code - 💡(How to fix) Fix Claude Desktop Cowork: VirtioFS mount fails on macOS because assemble() creates symlink instead of real directory for skills/ [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#51744Fetched 2026-04-22 07:54:00
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
labeled ×4

Error Message

RPC error: failed to mount The error message is confusing ("does not exist" + "file exists") because VirtioFS finds a symlink where it expects a real directory. 5. VirtioFS mount fails with the RPC error above

  • Error message is misleading and makes diagnosis very difficult

Root Cause

The error message is confusing ("does not exist" + "file exists") because VirtioFS finds a symlink where it expects a real directory.

Fix Action

Workaround

Manually replace the symlink with a real directory and apply chflags uchg to prevent assemble() from recreating it on subsequent session starts:

```bash SKILLS="$HOME/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin/<orgId>/<accountId>/skills" rm "$SKILLS" mkdir -m 700 "$SKILLS" chflags uchg "$SKILLS" ```

The uchg flag causes the unlink() inside assemble() to fail silently, leaving the real directory intact so VirtioFS can mount correctly.

RAW_BUFFERClick to expand / collapse

Platform

  • macOS (darwin arm64)
  • Claude Desktop (Cowork)

Bug Description

On macOS, the assemble() function in the Operon assembly code (class Umn in the compiled app.asar/index.js) contains this platform branch:

```javascript process.platform === "win32" ? await ce.cp(A, t, { recursive: true }) // Windows: real copy : await ce.symlink(A, t, "dir") // macOS/Linux: creates SYMLINK ```

This runs on every Cowork session start and replaces the directory at:

``` ~/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin/<orgId>/<accountId>/skills ```

…with a symlink pointing to ~/.claude/skills.

VirtioFS cannot mount through symlinks. The result is a hard failure every time a Cowork session starts:

``` RPC error: failed to mount /mnt/.virtiofs-root/shared/Users/.../skills-plugin/<org>/<account>/skills as .claude/skills: source path does not exist and could not be created: mkdir .../skills: file exists ```

The error message is confusing ("does not exist" + "file exists") because VirtioFS finds a symlink where it expects a real directory.

Steps to Reproduce

  1. Install Claude Desktop with Cowork on macOS
  2. Open any Cowork session — skills mount succeeds initially
  3. Close and reopen a Cowork session
  4. assemble() fires → replaces skills/ directory with a symlink
  5. VirtioFS mount fails with the RPC error above
  6. Cowork is unusable until the symlink is manually replaced with a real directory

Expected Behavior

assemble() should use cp (recursive copy) on macOS as it does on Windows, not symlink. Or alternatively, VirtioFS should be able to resolve symlinks before attempting to mount.

Workaround

Manually replace the symlink with a real directory and apply chflags uchg to prevent assemble() from recreating it on subsequent session starts:

```bash SKILLS="$HOME/Library/Application Support/Claude/local-agent-mode-sessions/skills-plugin/<orgId>/<accountId>/skills" rm "$SKILLS" mkdir -m 700 "$SKILLS" chflags uchg "$SKILLS" ```

The uchg flag causes the unlink() inside assemble() to fail silently, leaving the real directory intact so VirtioFS can mount correctly.

Impact

  • Cowork becomes permanently broken after the first session close/reopen cycle on macOS
  • Error message is misleading and makes diagnosis very difficult
  • No in-app recovery mechanism — requires manual filesystem intervention

extent analysis

TL;DR

The most likely fix is to modify the assemble() function to use cp (recursive copy) on macOS instead of creating a symlink.

Guidance

  • Identify the assemble() function in the Umn class and modify the platform branch to use ce.cp() for macOS, similar to the Windows implementation.
  • Verify that the assemble() function is correctly creating a real directory instead of a symlink by checking the file system after a Cowork session start.
  • Consider adding a check to ensure that the skills directory is not a symlink before attempting to mount it with VirtioFS.
  • If modifying the assemble() function is not feasible, apply the provided workaround to manually replace the symlink with a real directory and prevent assemble() from recreating it.

Example

process.platform === "win32"
  ? await ce.cp(A, t, { recursive: true })   // Windows: real copy
  : await ce.cp(A, t, { recursive: true });  // macOS: use recursive copy instead of symlink

Notes

The current implementation of assemble() uses a symlink on macOS, which is not compatible with VirtioFS. Modifying the function to use a recursive copy should resolve the issue. However, if this is not possible, the provided workaround can be used as a temporary solution.

Recommendation

Apply the workaround to manually replace the symlink with a real directory and prevent assemble() from recreating it, as modifying the assemble() function may require significant changes to the codebase. This will allow Cowork to function correctly on macOS until a more permanent solution can be implemented.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING