[BUG] Bug: File edits cause metadata loss (group, setuid, xattrs, hardlinks)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code's Edit and Write tools replace files via delete+create rather than in-place modification. The inode always changes after an edit. While basic rwx permission bits are recovered, several important pieces of file metadata are silently lost: group ownership, setuid/setgid/sticky bits, extended attributes, and hardlinks.
What Should Happen?
File edits should preserve all metadata. The standard approach is an atomic write-to-tempfile + rename(tmpfile, path), followed by copying metadata from the original:
fchownto restore uid + gidfchmodto restore the full mode including setuid/setgid/sticky bits- copy xattrs (iterate with
listxattr/setxattr)
Alternatively, open-and-truncate (open(path, O_WRONLY|O_TRUNC)) preserves inode identity (and thus hardlinks and xattrs), but sacrifices atomicity.
Error Messages/Logs
Steps to Reproduce
1. Group ownership is reset
echo "test" > foo.txt
chown user:docker foo.txt # set group to docker
# Edit foo.txt via Claude Code
stat foo.txt # Gid reverts to user's primary group
Before: Gid: ( 144/ docker)
After: Gid: ( 1001/ martin) ← silently reset
---
2. setuid/setgid/sticky bits are stripped
echo "test" > foo.py
chmod 4755 foo.py # -rwsr-xr-x
# Edit foo.py via Claude Code
stat foo.py # → 0755 -rwxr-xr-x (setuid gone)
Before: Access: (4755/-rwsr-xr-x)
After: Access: (0755/-rwxr-xr-x) ← setuid bit silently stripped
---
3. Extended attributes (xattrs) are lost
echo "test" > foo.txt
setfattr -n user.custom -v "hello" foo.txt
# Edit foo.txt via Claude Code
getfattr -d foo.txt # → (empty)
Before: user.custom="hello"
After: (no xattrs) ← xattr silently deleted
---
4. Hardlinks are broken
echo "test" > foo.txt
ln foo.txt bar.txt # both point to the same inode
# Edit foo.txt via Claude Code
cat bar.txt # → still shows old content
stat foo.txt # new inode
stat bar.txt # old inode — link is broken
Before: both files share one inode, Links: 2
After: two separate inodes, each with Links: 1 — the hardlink is silently broken and bar.txt diverges
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.145
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
_No response_
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗