Write tool drops group-write permission when overwriting existing files (umask bypassed via atomic rename)

Resolved 💬 2 comments Opened May 22, 2026 by lanphiergm Closed May 26, 2026

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 002 already set in the parent shell.
  • Process inheriting umask correctly — verified via umask, bash --norc --noprofile -c 'umask', sh -c 'umask', and python3 -c 'import os; m=os.umask(0); os.umask(m); print(oct(m))' — all report 0002.
  • The agent process runs as a different OS user (clanker) than the dev user (galen); both share group clanker, hence the reliance on group write for collaborative file access.

Reproduction

  1. Set umask 002 in your shell, launch claude.
  2. 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
``

  1. Ask Claude to populate it via the Write tool.
  2. Observe the result:

``
-rw-r--r-- 1 clanker clanker ... build-and-deploy.sh
`
Owner changed (expected — atomic rename creates a new inode), but group write is gone. With umask 002 and default 0o666, expected mode is
0o664`.

  1. By contrast, Write to a brand-new path under /tmp/claude/foo.txt correctly produces 0o664.

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:

  1. Have the atomic-rename path apply 0o666 & ~umask (mirroring fs.writeFile's behavior) rather than hardcoding 0o644.
  2. 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗