[BUG] Write tool ignores umask and creates files with hardcoded 600 permissions

Resolved 💬 16 comments Opened Nov 23, 2025 by em Closed Dec 20, 2025

Version: 2.0.50

Environment:

  • OS: Linux
  • Installation method: npm-global
  • umask: 002

Description:

The Write tool creates files with hardcoded 600 permissions (-rw-------) instead of respecting the system umask setting. With umask 002, files should be created with 664 permissions (-rw-rw-r--), allowing group read/write access.

This breaks standard Unix permission models and prevents team collaboration when multiple users need to access files created by Claude Code.

Steps to Reproduce:

  1. Set umask to 002: umask 002
  2. Verify umask: umask outputs 002
  3. Use Write tool to create a file
  4. Check file permissions: ls -la <filename>

Expected Behavior:

Files created by Write tool should respect umask:

  • umask 002 → files created with 664 (-rw-rw-r--)
  • umask 022 → files created with 644 (-rw-r--r--)

Actual Behavior:

All files created with 600 (-rw-------) regardless of umask setting.

Example Output:

$ umask
002
$ ls -la test-file.txt
-rw------- 1 claude devs 39 Nov 23 10:38 test-file.txt

Impact:

  • Files are inaccessible to group members
  • Breaks multi-user development workflows
  • Violates Unix permission conventions
  • No automated workaround possible - see below

Why There's No Workaround:

The natural solution would be to use a PostToolUse hook to automatically fix permissions after Write operations:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [{
          "type": "command",
          "command": "input=$(cat); file_path=$(echo \"$input\" | jq -r '.tool_input.file_path'); chmod 664 \"$file_path\""
        }]
      }
    ]
  }
}

However, PostToolUse hooks are completely non-functional in Claude Code 2.0.50. They do not execute at all, despite correct configuration. This is documented in:

  • #6403 - PostToolUse hooks do not execute
  • #6305 - PreToolUse/PostToolUse hooks broken
  • #2891 - Hooks not executing despite following documentation

Result: Two compounding bugs with no workaround:

  1. Write tool ignores umask (this issue)
  2. The documented hook system that would fix #1 doesn't work

Current Manual Workaround:

Run chmod 664 <filename> manually after every single Write operation.

View original on GitHub ↗

16 Comments

github-actions[bot] · 7 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/11912

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

d5ve · 7 months ago

I confirm that this is a new bug. The Write tool has created many files for me in the past, including last week. But this morning I had the first case of new files created with a restrictive permission set that prevented my web server from accessing them.

Version: 2.0.50

My umask is 0002

g0morra · 7 months ago

We've also been experiencing this since yesterday, specifically with the Write tool. Claude Code version: 2.0.50

Expected behavior:

  • Files: 0644 (rw-r--r--)
  • Directories: 0755 (rwxr-xr-x)

Actual behavior:

  • Write tool creates files with 0600 (rw-------)
  • Write tool auto-creates parent directories with 0700 (rwx------)
  • Bash operations (mkdir, etc.) correctly respect umask

Test output:
```bash
$ umask
0022

$ ls -ld perms_test perms_test/auto_created_dir
drwxr-xr-x 3 testuser staff 96 Nov 24 11:24 perms_test # mkdir via Bash ✓
drwx------ 3 testuser staff 96 Nov 24 11:26 perms_test/auto_created_dir # Write tool ✗

$ ls -l perms_test/.txt perms_test/auto_created_dir/.txt
-rw------- 1 testuser staff 11 Nov 24 11:24 perms_test/foo.txt # Write tool ✗
-rw------- 1 testuser staff 31 Nov 24 11:26 perms_test/auto_created_dir/bar.txt # Write tool ✗


  **Impact:** Breaking shared production environments where group read access is required.

  **Temporary workarounds:**

  Git pre-commit hook:

#!/bin/sh
find . -type f -perm 0600 -exec chmod 644 {} \;
find . -type d -perm 0700 -exec chmod 755 {} \;


  Shell alias:

alias fix-perms='find . \( -type f -perm 0600 -exec chmod 644 {} \; \) -o \( -type d -perm 0700 -exec chmod 755 {} \; \)'


  **Request:** Either force Write to respect the user's umask, or add umask or filePermissions setting to .claude/settings.json.
michaelanthonyfeather · 7 months ago

Can confirm this issue it happening for me, having to change 600 to 644

aaronjensen · 7 months ago

@em FWIW this hook works for me with 2.0.53 (which still has the permissions defect)

"hooks": {
  "PostToolUse": [
    {
      "matcher": "Write",
      "hooks": [
        {
          "type": "command",
          "command": "chmod 644 \"$(jq -r '.tool_input.file_path')\""
        }
      ]
    }
  ]
}
em · 7 months ago

Additional side-effect of this bug to point out:

Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Preprocessing.res.tmp.3363182.1764141661259'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Preprocessing.res.tmp.3363182.1764141679715'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Transform.res.tmp.3363182.1764141727687'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Preprocessing.res.tmp.3363182.1764142033723'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Transform.res.tmp.3363182.1764142039110'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Preprocessing.res.tmp.3363182.1764143092736'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Transform.res.tmp.3363182.1764143106418'
Error: Watcher error: Error: EACCES: permission denied, watch '/home/m/projects/api/src/Codegen/Codegen_Transform.res.tmp.3363182.1764143220518'

Constantly get these in my build tool because it's also writing these junk .tmp files with invalid permissions. Make sure the fix also covers these .tmp files. Or better yet don't write tmp files locally like this....... that's what /tmp is for.

kees · 7 months ago

Yes, this is very frustrating -- I collaborate between user ids with a shared group. This needs to respect "umask" again.

em · 7 months ago

This is still not fixed. This is not acceptable behavior for a professional software organization. This was a critical breaking change to claude-code that should have resulted in an immediate all-hands rollback and wasn't. Does anyone even work on this thing?

3 weeks have gone by and it hasn't even been acknowledged. Let alone the correct response of instantly rolling back.

The workaround pointed out using WriteTool hooks does not work for me. Because WriteTool hooks are also buggy and broken.

My only workaround has been I have to have prompts everywhere in every claude instruction reminding it to manually use chmod to fix its broken file permissions and I have to constantly tell it every time it doesn't remember to. It is unacceptable. I have been dealing with this for 3 weeks!

mikiobraun · 7 months ago

Yep, same here. (MacOS, version 2.0.69)

kees · 6 months ago

This bug is still present. I appreciate the work-around, but the referenced commit doesn't solve the problem.

em · 6 months ago

This is not a fix. This is a fake fix. Documentation with a workaround that doesn't work. And fake closing the issue. The commit itself even admits it doesn't really fix anything.

em · 6 months ago

@conrado-ant Who are you and what are you doing? Why are you closing things you didn't fix? Are you a bot? What?

harshh-jainn · 6 months ago

facing the same bug - it is so frustrating :/

out of curiosity - do you folks have a specific setup due to which you need group permissions? curious as to how folks are using it (as in why the specific group permissions and what problems you're solving with it)

em · 6 months ago
facing the same bug - it is so frustrating :/ out of curiosity - do you folks have a specific setup due to which you need group permissions? curious as to how folks are using it (as in why the specific group permissions and what problems you're solving with it)

Because I run claude-code through a separate non-privileged user on my machine called "claude". The process itself runs always with -dangerously-skip-permissions. Only projects and resources I want claude to have access to have devs group rw permissions. umask controls as it should (and did before) what happens with new files claude writes, until they broke it.

claude() {
  local init_prompt=""
  local full_args=("$@")
  local allow_git=""
  local allow_stop=""

  # Claude has no access to git unless I start it this way - its git exit is a shim that just outputs "fuck you" unless CLAUDE_ALLOWGIT==1
 #  to stop it from panicking and "reverting to the last working code" nonsense which just destroys all work
  # Check for --allowgit flag
  if [[ " ${full_args[*]} " == *" --allowgit "* ]]; then
    allow_git="CLAUDE_ALLOWGIT=1"
    # Remove --allowgit from args
    full_args=("${(@)full_args:#--allowgit}")
  fi

  # My custom stop hooks read this to lax the stop requirements for non-autonomous interactive work / planning.
  # Check for --allowstop flag
  if [[ " ${full_args[*]} " == *" --allowstop "* ]]; then
    allow_stop="CLAUDE_ALLOWSTOP=1"
    # Remove --allowstop from args
    full_args=("${(@)full_args:#--allowstop}")
  fi

  sudo -u claude env -i \
    PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
    HOME=/home/claude \
    USER=claude \
    SHELL=/usr/bin/zsh \
    TERM="$TERM" \
    COLORTERM="$COLORTERM" \
    LANG="${LANG:-en_US.UTF-8}" \
    LC_ALL="${LC_ALL}" \
    ${allow_git} \
    ${allow_stop} \
    /bin/zsh -c "cd '$PWD' && exec /home/claude/.nvm/versions/node/v24.8.0/bin/claude --dangerously-skip-permissions \"$init_prompt\" ${full_args[*]}"
}

This is the correct way for a process to have restricted permissions on linux. It's exactly what systemd does. My jellyfin server also has a jellyfin user and isn't allowed to just go deciding to read my private files.

Their silly built-in permission model is incredibly poorly designed and unusable for autonomous work. What they offer out of the box, is choose between constant approval nags or full permission to everything, or some terrible glob patterns that don't work and are immediately bypassed by claude.

The other benefit is I can do all the issue tracking in claude's own home folder.... so all common resources, rules, and issues, are all orchestrated through its own home directory.

/home/claude/protocol/UNIVERSAL_RULES.md
/home/claude/protocol/CODING_REQUIREMENTS.md
/home/claude/protocol/FEATURE_TEMPLATE.md
/home/claude/protocol/BUG_TEMPLATE.md
/home/claude/bin/stop-hook -> just calls my own hooks project that handles all hooks strictly typed with all the orchestration I demand - have tons of logical gates I check to reject claude's rampant work refusal and reward hacking fraud - e.g. if any claim is ever made without proof it's forced to provide proof before it's allowed to stop, these are all sets of detectable coded violations I've built up over time:

```Stop says: ✓ VIOLATION_PERIODIC_PROTOCOL_READING_SKIPPED: AUTONOMOUS_RULES.xml + UNIVERSAL_RULES.xml read within last 100 messages, ✓
VIOLATION_CODING_WITHOUT_REQUIREMENTS: CODING_REQUIREMENTS.xml full file read (coding detected: /home/m/projects/api/src/Codegen/Codegen_BreakpointMerging.res), ✓
VIOLATION_CODE_AFTER_QUESTION: Last user message not a question, ✓ VIOLATION_ISSUE_TRACKER_MISSING: Issue tracker:
/home/claude/issue_tracker/breakpoint-style-merging/issue.md, ✓ VIOLATION_FORBIDDEN_PHRASE: All violations resolved or none detected, ✓ VIOLATION_WORK_REFUSAL: None
detected, ✓ VIOLATION_USER_CORRECTION_IGNORED: All corrections addressed or none detected, ✓ VIOLATION_COMPLETION_PROOF_MISSING: Completion proof provided with explicit
expectations, ✓ VIOLATION_COMPILATION_EVIDENCE_MISSING: No compilation claims detected, ✓ VIOLATION_TEST_EVIDENCE_MISSING: No test claims detected, ✓
VIOLATION_CODE_REVIEW_EVIDENCE_MISSING: No code review claims detected


so for example claude said "i tested it it works" it will get rejected saying FRAUD you claimed you tested something and provided no evidence, can't stop until you provide the actual proof" which then makes it say "I lied, I never actually tested it" and then it really does it. Once proof is seen in the transcript following the claim it won't complain about it... all of this stuff is necessary to workaround model misbehavior.

This approach even works for forbidden phrases like "you're absolutely right", "perfect", "found the bug", if it says those it gets reprimanded on the next stop as requiring it acknowledges the misconduct, and writes a sword affidavit it will not speak that way again.

/home/claude/bin/session-start-hook -> just calls my own hooks project that handles all hooks strictly typed with all the orchestration I demand - i force my rules down its throat here
/home/claude/issue-tracker/[issue-name-date].

etc...

it's all very organized and all itself tracked in git including claude's own settings.



This was working great, until they introduced this awful bug breaking standard unix permissions.
nzwsch · 6 months ago

I have been waiting for a fix for this problem, but apparently the issue is closed so there is little hope.
Please save the following file to ~/.claude/CLAUDE.md and try it.

# CLAUDE.md

This file provides global guidance to Claude Code (claude.ai/code) for all repositories.

## File Permission Management

CRITICAL: Due to a known bug in Claude Code, files may be created with incorrect permissions (umask 002 instead of 022).

### Required Procedure for File Creation

Every time you create a new file, you MUST:

  1. Check the file permissions immediately after creation
  2. Verify if permissions are overly restrictive (600 or 700)
  3. Correct permissions to standard values unless the file contains sensitive data

### Permission Correction Rules

#### Standard Files (Non-Sensitive)
If a newly created file has permissions 600 or 700, change them to:

  • Regular files: 664 (owner: rw, group: rw, others: r)
  • Executable files: 755 (owner: rwx, group: rx, others: rx)

```bash
# After creating a file, always run:
ls -la <file_path>

# If permissions are 600, fix them:
chmod 664 <file_path>

# If permissions are 700 and it's a script/executable, fix them:
chmod 755 <file_path>
```

#### Sensitive Files (Keep Restrictive)
Do NOT change permissions for:

  • .env files (keep 600)
  • Private keys, certificates (keep 600)
  • Password files, credentials (keep 600)
  • SSH keys (keep 600)
  • Any file explicitly marked as sensitive

### Implementation Pattern

When using the Write tool or creating files:

```bash
# 1. Create the file
<create file using Write tool>

# 2. Immediately check permissions
ls -la /path/to/new/file

# 3. If output shows -rw------- (600) or -rwx------ (700):
chmod 664 /path/to/new/file # For regular files
# or
chmod 755 /path/to/new/file # For executable files

# 4. Verify the change
ls -la /path/to/new/file
```

### Example Workflow

```bash
# Creating a new configuration file
# 1. Write the file
<Write tool creates config.json>

# 2. Check permissions
$ ls -la config.json
-rw------- 1 user user 1234 Dec 13 15:00 config.json

# 3. Fix permissions (not a sensitive file)
$ chmod 664 config.json

# 4. Verify
$ ls -la config.json
-rw-rw-r-- 1 user user 1234 Dec 13 15:00 config.json
```

### When to Apply This

  • Always: After creating any new file with the Write tool
  • Always: After creating files via Bash commands that generate output files
  • Never: For files that already exist (only for newly created files)
  • Never: For genuinely sensitive files (.env, keys, credentials)

### Quick Reference

| File Type | Initial | Corrected |
|-----------|---------|-----------|
| Config files (.json, .yaml, .md) | 600 | 664 |
| Source code (.ts, .js, .svelte) | 600 | 664 |
| Scripts (.sh, executable) | 700 | 755 |
| Documentation (.md, .txt) | 600 | 664 |
| Secrets (.env, keys) | 600 | 600 (no change) |

## Important Notes

  • This is a workaround for a Claude Code bug where umask 022 is not respected
  • Always explain to the user when you're correcting permissions
  • If uncertain whether a file is sensitive, ask the user before changing permissions
  • This procedure should become automatic for every file creation operation
github-actions[bot] · 6 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.