Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

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:

  1. Writing commands to a temporary file (e.g., fish_history.tmp.XXXXXX)
  2. 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#

  1. 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"
    
  2. Mount Type: fakeowner

    /run/host_mark/Users /home/me/.local/share/fish/fish_history fakeowner rw,nosuid,nodev,relatime,fakeowner 0 0
    

    The fakeowner filesystem is used by Docker Desktop on macOS/Windows to emulate Linux file ownership. It has limitations with atomic file operations.

  3. Cross-Filesystem Rename Limitation

    • The temp file is created in /home/me/.local/share/fish/ (container's native filesystem)
    • The target fish_history is on the bind-mounted fakeowner filesystem
    • Atomic rename across different filesystems fails
  4. Multiple Fish Processes

    • VS Code's integrated terminal
    • The 🐟-fishy buffer 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_history on 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.


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#

  1. Open VS Code with the devcontainer
  2. Run commands in the integrated terminal (fish)
  3. Simultaneously open another fish terminal or run commands from Emacs
  4. 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.



Last updated: December 19, 2025