Fish "Device or Resource Busy" Error Investigation#
The Error#
error: Error when renaming file: Device or resource busy
This error appears sporadically when running fish commands in the devcontainer.
Root Cause Analysis#
The Mechanism#
Fish shell saves command history by:
- Writing commands to a temporary file (e.g.,
fish_history.tmp.XXXXXX) - Using
rename()syscall to atomically replace the old file
The 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.
Why It Happens in This Devcontainer#
-
Bind Mount for fish_history
// devcontainer.json line 24 "source=${localWorkspaceFolder}/.devcontainer/fish_history,target=/home/me/.local/share/fish/fish_history,type=bind,consistency=cached" -
Mount Type:
fakeowner/run/host_mark/Users /home/me/.local/share/fish/fish_history fakeowner rw,nosuid,nodev,relatime,fakeowner 0 0The
fakeownerfilesystem is used by Docker Desktop on macOS/Windows to emulate Linux file ownership. It has limitations with atomic file operations. -
Cross-Filesystem Rename Limitation
- The temp file is created in
/home/me/.local/share/fish/(container's native filesystem) - The target
fish_historyis on the bind-mountedfakeownerfilesystem - Atomic rename across different filesystems fails
- The temp file is created in
-
Multiple Fish Processes
- VS Code's integrated terminal
- The
🐟-fishybuffer in Emacs - Any other fish shell opened
- All compete to write history simultaneously
Why It's Intermittent#
The error only occurs when:
- A command finishes executing AND
- Fish attempts to save history AND
- Another process has the history file open OR
- The fakeowner filesystem rejects the rename
Most of the time it succeeds because:
- Commands execute quickly
- History save completes before conflict
- The filesystem cooperates
Current Mitigations in Place#
1. In Dockerfile (line 257-264)#
# Ensure fish doesn't try to use universal variable daemon (causes permission issues)
ENV fish_color_host_remote=""
RUN mkdir -p /home/me/.config/fish/conf.d && \
echo 'set -U fish_greeting ""' > /home/me/.config/fish/conf.d/greeting.fish
2. In entry.fish (line 139-141)#
# Disable fish's universal variable file daemon (fishd) which causes permission issues
# We'll use fish_variables instead which is simpler and doesn't create temp files
set -U fish_greeting ""
3. Permission Fixes (entry.fish line 108-142)#
function ensure_fish_config_permissions
# ... aggressive chmod/chown fixes
end
Potential Solutions#
Option 1: Stop Persisting Fish History (Simplest)#
Remove the bind mount entirely — history won't survive container rebuilds but the error goes away.
// Remove this line from devcontainer.json:
"source=${localWorkspaceFolder}/.devcontainer/fish_history,target=/home/me/.local/share/fish/fish_history,type=bind,consistency=cached",
Pros: Error completely eliminated
Cons: Lose command history between rebuilds
Option 2: Use a Volume Instead of Bind Mount#
"source=aesthetic-fish-history,target=/home/me/.local/share/fish,type=volume"
Pros: Better filesystem semantics, still persists
Cons: History isolated per Docker volume, not in git repo
Option 3: Disable Fish History Entirely#
Add to config.fish:
set -g fish_history ""
Pros: No history writes at all
Cons: No up-arrow history
Option 4: Periodic Sync Instead of Direct Mount#
- Let fish write to native filesystem
- Sync to
.devcontainer/fish_historyon container stop - Restore on container start
This is complex but preserves history without the rename errors.
Option 5: Suppress the Error (Cosmetic Fix)#
Wrap fish invocations to hide stderr containing this specific error. Doesn't fix the underlying issue.
Recommended Action#
Short-term: Accept the error as cosmetic — it doesn't affect functionality, history still works most of the time.
Medium-term: Switch to Option 2 (volume mount) if the error becomes annoying:
// Replace the fish_history bind mount with:
"source=aesthetic-fish-history,target=/home/me/.local/share/fish,type=volume"
Long-term: Consider Option 4 (periodic sync) for the best of both worlds — works reliably AND preserves history in git.
Files Involved#
| File | Purpose |
|---|---|
.devcontainer/devcontainer.json |
Defines the bind mount (line 24) |
.devcontainer/fish_history |
The persisted history file |
.devcontainer/entry.fish |
Container startup, permission fixes |
.devcontainer/config.fish |
Fish shell configuration |
.devcontainer/Dockerfile |
Base image, fish installation |
Reproduction#
- Open VS Code with the devcontainer
- Run commands in the integrated terminal (fish)
- Simultaneously open another fish terminal or run commands from Emacs
- Eventually, one of them will show the error when saving history
The error is benign — the command still executes, only the history save fails for that one invocation.
Related Fish Issues#
- https://github.com/fish-shell/fish-shell/issues/5658 — Atomic rename on network filesystems
- https://github.com/fish-shell/fish-shell/issues/3845 — History file locking
- https://github.com/microsoft/vscode-remote-release/issues/2347 — Bind mount limitations
Last updated: December 19, 2025