Monorepo for Aesthetic.Computer
aesthetic.computer
1# Fish "Device or Resource Busy" Error Investigation
2
3## The Error
4```
5error: Error when renaming file: Device or resource busy
6```
7
8This error appears sporadically when running fish commands in the devcontainer.
9
10---
11
12## Root Cause Analysis
13
14### The Mechanism
15
16Fish shell saves command history by:
171. Writing commands to a **temporary file** (e.g., `fish_history.tmp.XXXXXX`)
182. Using `rename()` syscall to atomically replace the old file
19
20The error occurs when the `rename()` syscall fails because the **target file is busy** — meaning another process has it open or the underlying filesystem doesn't support atomic rename on bind-mounted files.
21
22### Why It Happens in This Devcontainer
23
241. **Bind Mount for fish_history**
25 ```jsonc
26 // devcontainer.json line 24
27 "source=${localWorkspaceFolder}/.devcontainer/fish_history,target=/home/me/.local/share/fish/fish_history,type=bind,consistency=cached"
28 ```
29
302. **Mount Type: `fakeowner`**
31 ```
32 /run/host_mark/Users /home/me/.local/share/fish/fish_history fakeowner rw,nosuid,nodev,relatime,fakeowner 0 0
33 ```
34
35 The `fakeowner` filesystem is used by Docker Desktop on macOS/Windows to emulate Linux file ownership. It has limitations with atomic file operations.
36
373. **Cross-Filesystem Rename Limitation**
38 - The temp file is created in `/home/me/.local/share/fish/` (container's native filesystem)
39 - The target `fish_history` is on the bind-mounted `fakeowner` filesystem
40 - Atomic rename across different filesystems fails
41
424. **Multiple Fish Processes**
43 - VS Code's integrated terminal
44 - The `🐟-fishy` buffer in Emacs
45 - Any other fish shell opened
46 - All compete to write history simultaneously
47
48---
49
50## Why It's Intermittent
51
52The error only occurs when:
53- A command finishes executing AND
54- Fish attempts to save history AND
55- Another process has the history file open OR
56- The fakeowner filesystem rejects the rename
57
58Most of the time it succeeds because:
59- Commands execute quickly
60- History save completes before conflict
61- The filesystem cooperates
62
63---
64
65## Current Mitigations in Place
66
67### 1. In Dockerfile (line 257-264)
68```dockerfile
69# Ensure fish doesn't try to use universal variable daemon (causes permission issues)
70ENV fish_color_host_remote=""
71RUN mkdir -p /home/me/.config/fish/conf.d && \
72 echo 'set -U fish_greeting ""' > /home/me/.config/fish/conf.d/greeting.fish
73```
74
75### 2. In entry.fish (line 139-141)
76```fish
77# Disable fish's universal variable file daemon (fishd) which causes permission issues
78# We'll use fish_variables instead which is simpler and doesn't create temp files
79set -U fish_greeting ""
80```
81
82### 3. Permission Fixes (entry.fish line 108-142)
83```fish
84function ensure_fish_config_permissions
85 # ... aggressive chmod/chown fixes
86end
87```
88
89---
90
91## Potential Solutions
92
93### Option 1: Stop Persisting Fish History (Simplest)
94Remove the bind mount entirely — history won't survive container rebuilds but the error goes away.
95
96```jsonc
97// Remove this line from devcontainer.json:
98"source=${localWorkspaceFolder}/.devcontainer/fish_history,target=/home/me/.local/share/fish/fish_history,type=bind,consistency=cached",
99```
100
101**Pros**: Error completely eliminated
102**Cons**: Lose command history between rebuilds
103
104### Option 2: Use a Volume Instead of Bind Mount
105```jsonc
106"source=aesthetic-fish-history,target=/home/me/.local/share/fish,type=volume"
107```
108
109**Pros**: Better filesystem semantics, still persists
110**Cons**: History isolated per Docker volume, not in git repo
111
112### Option 3: Disable Fish History Entirely
113Add to `config.fish`:
114```fish
115set -g fish_history ""
116```
117
118**Pros**: No history writes at all
119**Cons**: No up-arrow history
120
121### Option 4: Periodic Sync Instead of Direct Mount
122- Let fish write to native filesystem
123- Sync to `.devcontainer/fish_history` on container stop
124- Restore on container start
125
126This is complex but preserves history without the rename errors.
127
128### Option 5: Suppress the Error (Cosmetic Fix)
129Wrap fish invocations to hide stderr containing this specific error. Doesn't fix the underlying issue.
130
131---
132
133## Recommended Action
134
135**Short-term**: Accept the error as cosmetic — it doesn't affect functionality, history still works most of the time.
136
137**Medium-term**: Switch to **Option 2** (volume mount) if the error becomes annoying:
138```jsonc
139// Replace the fish_history bind mount with:
140"source=aesthetic-fish-history,target=/home/me/.local/share/fish,type=volume"
141```
142
143**Long-term**: Consider **Option 4** (periodic sync) for the best of both worlds — works reliably AND preserves history in git.
144
145---
146
147## Files Involved
148
149| File | Purpose |
150|------|---------|
151| `.devcontainer/devcontainer.json` | Defines the bind mount (line 24) |
152| `.devcontainer/fish_history` | The persisted history file |
153| `.devcontainer/entry.fish` | Container startup, permission fixes |
154| `.devcontainer/config.fish` | Fish shell configuration |
155| `.devcontainer/Dockerfile` | Base image, fish installation |
156
157---
158
159## Reproduction
160
1611. Open VS Code with the devcontainer
1622. Run commands in the integrated terminal (fish)
1633. Simultaneously open another fish terminal or run commands from Emacs
1644. Eventually, one of them will show the error when saving history
165
166The error is **benign** — the command still executes, only the history save fails for that one invocation.
167
168---
169
170## Related Fish Issues
171
172- https://github.com/fish-shell/fish-shell/issues/5658 — Atomic rename on network filesystems
173- https://github.com/fish-shell/fish-shell/issues/3845 — History file locking
174- https://github.com/microsoft/vscode-remote-release/issues/2347 — Bind mount limitations
175
176---
177
178*Last updated: December 19, 2025*