Write tool drops group-write permission when overwriting existing files (umask bypassed via atomic rename)
Write tool drops group-write permission when overwriting existing files (umask bypassed via atomic rename)
Summary
When the Write tool overwrites an existing file, the resulting file's mode appears to be hardcoded to 0o644, ignoring the process's umask. Creating a file at a fresh path correctly respects umask. This breaks workflows where users set umask 002 so collaborators sharing a group can write to each other's files.
Environment
- Platform: macOS (Darwin 25.3.0)
- Claude Code launched from a bash terminal with
umask 002already set in the parent shell. - Process inheriting umask correctly — verified via
umask,bash --norc --noprofile -c 'umask',sh -c 'umask', andpython3 -c 'import os; m=os.umask(0); os.umask(m); print(oct(m))'— all report0002. - The agent process runs as a different OS user (
clanker) than the dev user (galen); both share groupclanker, hence the reliance on group write for collaborative file access.
Reproduction
- Set
umask 002in your shell, launchclaude. - Have an existing file owned by another user in the same group, e.g.:
````
-rw-rw-r-- 1 galen clanker 0 build-and-deploy.sh
- Ask Claude to populate it via the Write tool.
- Observe the result:
```
-rw-r--r-- 1 clanker clanker ... build-and-deploy.sh
0o664`.
Owner changed (expected — atomic rename creates a new inode), but group write is gone. With umask 002 and default 0o666, expected mode is
- By contrast, Write to a brand-new path under
/tmp/claude/foo.txtcorrectly produces0o664.
Likely cause
The behavior is consistent with an atomic-rename pattern: Write creates a temp file (probably via mkstemp, which produces mode 0o600), then chmods it to an explicit 0o644 before rename-ing over the destination. Hardcoding the mode at this step bypasses the process umask.
The fresh-path code path appears to use fs.writeFile directly, which does respect umask (default mode 0o666 & ~umask → 0o664 with umask 002).
Suggested fix
Two reasonable options:
- Have the atomic-rename path apply
0o666 & ~umask(mirroringfs.writeFile's behavior) rather than hardcoding0o644. - When overwriting an existing file, preserve the destination's existing mode bits across the rename. This is arguably more correct since users who set explicit modes (e.g.,
chmod 600 secrets.env) would expect those to survive an edit.
(2) is closer to the principle of least surprise but requires a stat before write. (1) is a smaller change and consistent with the fresh-path behavior.
Impact
- Breaks collaborative-group workflows where multiple users (or a user + an agent running as a different OS user) need shared write access via group permissions.
- Silent — there's no indication that umask was bypassed, so it's only noticed when a collaborator hits "permission denied" later.
- Workaround:
chmod g+w <file>after every Write/Edit. Tedious and easy to forget.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗