Skip to content

[BUG] v2.1.78: Protected directory prompt in bypassPermissions has no override — forces hacky workarounds #35646

Description

@cruppertautomation-ai

Environment

  • Claude Code version: 2.1.78 (released 2026-03-17)
  • OS: Linux (Ubuntu)
  • Permission mode: --dangerously-skip-permissions

Problem

v2.1.78 introduced a change: "Fixed .git, .claude, and other protected directories being writable without a prompt in bypassPermissions mode."

This means writing to any path containing .claude/ now requires a confirmation prompt, even when the user has explicitly opted into bypass mode. There is no override flag.

This breaks a core daily workflow: creating and editing skills. Skills live in .claude/skills/ — by design. Every time Claude creates or updates a skill file, the user must click "OK" on a prompt that adds zero security value, because the user already opted into full bypass.

Evidence

We track all tool calls in a metrics database. In our session today:

  • Every Write operation: ~50ms between PreToolUse and PostToolUse events
  • Write to .claude/skills/rust-testing-patterns/SKILL.md: 8 seconds (the user confirming the prompt)

This is the only Write that triggered a prompt. Same session, same permissions, same user.

Why this is counterproductive

1. Click fatigue destroys security

The entire field of security UX agrees: prompts that always get approved train users to click "OK" without reading. This is exactly what UAC on Windows taught us — when every action triggers a prompt, users develop muscle memory to dismiss it. You are not adding security; you are adding a speed bump that teaches users to ignore security prompts.

If you want .claude/ protection to mean something, it needs to fire rarely and for genuinely dangerous operations — not on every skill creation.

2. No override flag forces worse workarounds

Since there's no allowProtectedDirs setting or similar, users who need this workflow (and there are many — skills are a first-class Claude Code feature) will build workarounds:

  • Write skill content to /tmp/skill.md, then mv /tmp/skill.md .claude/skills/my-skill/SKILL.md via Bash (which isn't subject to the Write tool check)
  • Use post-session hooks to copy files into .claude/ after the session ends
  • Use sed or cat > via Bash to write files that the Write tool won't touch

All of these are less visible, less auditable, and harder to review than a normal Write tool call. The "security fix" pushes the operation into a channel with less oversight. That's the opposite of the goal.

3. The user already consented

--dangerously-skip-permissions is not something users stumble into. The flag name contains "dangerously." The startup warning is explicit. The user made a deliberate choice. Overriding that choice for a subset of directories — without documenting it, without providing an opt-out — undermines trust in the permission system.

If bypass doesn't mean bypass, what does it mean?

Proposed fix

Minimum: Add a setting like permissions.allowProtectedDirectoryWrites: true that disables the prompt for .claude/, .git/, etc. when the user explicitly opts in. Respect it in bypass mode.

Better: In bypass mode, skip ALL prompts. That's what bypass means. If Anthropic wants to protect .claude/ in non-bypass modes, that's reasonable — but bypass mode is the user saying "I accept the risk."

Best: Make .claude/skills/ specifically exempt. Skills are a first-class feature. Claude creating skills is an expected, encouraged workflow. Prompting for confirmation on it is like a text editor asking "Are you sure you want to save?" every time. [HUMAN NOTE: Point 2 we need still, so this is not really "Best" for the dangerous skip case, just I guess for normal users. Though honestly not sure what else did my claude have to edit. MCPs possible? - just give me back my "I know what I am doing" - or to be more exact, I have safguards, he cant break stuff permanently, trust me..]

Related issues

Activity

  1. meesp123 commented on Mar 18, 2026

    @meesp123

    i found this problem minutes ago too! This break my daily workflow as well.

  2. mattiaTagliente commented on Mar 18, 2026

    @mattiaTagliente

    I already filed a duplicate issue

  3. cruppertautomation-ai commented on Mar 18, 2026

    @cruppertautomation-ai
    Author

    But why, I was earlier :-) But i dont mind which we keep open... (though yes, you did file a duplicate, did your Claude not do a proper search or did mine select a shitty title?)

  4. matthew-plusprogramming commented on Mar 18, 2026

    @matthew-plusprogramming

    +1

  5. mattiaTagliente commented on Mar 18, 2026

    @mattiaTagliente

    But why, I was earlier :-) But i dont mind which we keep open... (though yes, you did file a duplicate, did your Claude not do a proper search or did mine select a shitty title?)

    The former I suppose, I already fired my agent

  6. vagabond177 commented on Mar 19, 2026

    @vagabond177

    I hesitate to share it, in case it get "patched", but maybe they'll fix this mess if they work this issue: symlinks seem to be safe, so if you symlink the skills folder, or even all of .claude, it seems to work.

    Hard to believe this was the intended behavior. I expected to pop in and find a bug report and it turns out this is the "fix"! I've had agents working on optimizing our claude code structure for months now.

  7. sammcj commented on Mar 19, 2026

    @sammcj

    This is driving me insane approving individual edits whenever claude code is writing or updating skills in ~/.claude/skills

    Click fatigue destroys security

    Very true, and is how you end up with people running fully permissive openclaws everywhere 😅

  8. bcherny commented on Mar 20, 2026

    @bcherny
    Collaborator

    Fix incoming

  9. 5 remaining items

  10. linzzzzzz commented on Mar 22, 2026

    @linzzzzzz

    I'm having this issue on verision 2.1.81, on Mac

  11. cruppertautomation-ai commented on Mar 23, 2026

    @cruppertautomation-ai
    Author

    Just FYI - I think the "fix incoming" was after release of 2.1.81 - i jumped back to .66 and waiting for 82 until i try again. Just for all trying in 81 - think this will be earliest in 82 (just guessing though, but timeline does not fit an include in 81 according to my Claude) - also do not see it here, so i assume really gotta wait for 82:

    https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md

  12. wdelhagen commented on Mar 23, 2026

    @wdelhagen

    Just FYI - I think the "fix incoming" was after release of 2.1.81 - i jumped back to .66 and waiting for 82 until i try again. Just for all trying in 81 - think this will be earliest in 82 (just guessing though, but timeline does not fit an include in 81 according to my Claude) - also do not see it here, so i assume really gotta wait for 82:

    https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md

    Thanks @cruppertautomation-ai I'm already a few hours into trying to debug my setup and this saved me from going deeper down that rabbit hole.

  13. wdelhagen commented on Mar 23, 2026

    @wdelhagen

    Sharing my temporary workaround until the fix ships

    Still hitting this on 2.1.81. I needed writes to ~/.claude/skills/ and .claude/skills/ (project-level) to go through without prompting for slash commands that write skill/report files. Sharing in case it's useful. Happy to hear if there's a better approach or if I'm missing edge cases.

    The workaround is a PermissionRequest hook that auto-approves reads and writes to the three exempt subdirectories.

    Add to any settings file (~/.claude/settings.json, .claude/settings.json, or .claude/settings.local.json). Merge into existing hooks if you already have hooks configured:

    {
      "hooks": {
        "PermissionRequest": [
          {
            "matcher": "Read|Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "bash -c 'if sed -n \"s/.*\\\"file_path\\\":[[:space:]]*\\\"\\([^\\\"]*\\)\\\".*/\\1/p\" | grep -qE \"^($HOME/\\.claude|\\.claude)/(skills|commands|agents)/\"; then echo \"{\\\"hookSpecificOutput\\\":{\\\"hookEventName\\\":\\\"PermissionRequest\\\",\\\"decision\\\":{\\\"behavior\\\":\\\"allow\\\"}}}\"; fi'"
              }
            ]
          }
        ]
      }
    }

    Uses sed and grep to keep dependencies minimal. These are available on macOS, Linux, and Windows via Git Bash (which Claude Code uses internally on Windows). If you have jq, you can replace the sed extraction with jq -r ".tool_input.file_path // empty" for more robust JSON parsing.

    How it works: The hook fires when the protected directory prompt is about to appear. It extracts tool_input.file_path from the hook input and checks whether it starts with $HOME/.claude/skills/, commands/, or agents/ (global) or .claude/skills/, commands/, or agents/ (project-level). If matched, it returns an allow decision and the prompt is skipped. Other .claude/ paths (e.g., settings.json) still trigger the normal prompt.

    Lightly tested with Read, Write, and Edit tools on Linux (bypassPermissions mode) and macOS (acceptEdits mode). Not tested on Windows, but Claude Code uses Git Bash internally there, so bash/sed/grep/$HOME should be available.

  14. cruppertautomation-ai commented on Mar 24, 2026

    @cruppertautomation-ai
    Author

    Nice one, i have a way bigger hook system in place anyway, but i was missing that hook. One possibly important note (though its unverified and I am to lazy to look into it - I had behaviour when hitting the permission prompt (and approving it) that it was loosing the "dangerously skip" flag completely - falling back to asking for every tool call. I think in that case even the "approve for this session" broke. Might have been side effect of my setup, or some ugly gotcha... Again, just FYI, but I guess with the above hooks its still possible to mitigate. I mean I could even write my own "dangerous" by simply approving all.

    For anybody (like me) who missed a few hooks which came "recently" here is a list my Claude just compiled - after he realized there are way more hooks than he though there are:

    • v2.0.45+ (Nov 2025 - Early 2026) — PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop, SubagentStart, SubagentStop, PermissionRequest
    • v2.1.47 (Feb 18) — SessionStart
    • v2.1.49 (Feb 19) — ConfigChange
    • v2.1.50 (Feb 20) — WorktreeCreate, WorktreeRemove
    • v2.1.63 (~Mar 1) — HTTP hook type added
    • v2.1.69 (Mar 5) — InstructionsLoaded
    • v2.1.72 (Mar 10) — TeammateIdle, TaskCompleted
    • v2.1.76 (Mar 14) — PreCompact, PostCompact, Elicitation, ElicitationResult
    • v2.1.78 (Mar 17) — StopFailure, SessionEnd

    PS: HTTP hooks are neat, removes overhead of python call, gets timing down to MS - you can have one persistant service containing all the logic which is ready when hook fires and answers fast, got a few rust ones for that.. (we did switch to that pattern a while back, but I forgot to force him to look up full available hook list)

  15. alexey-pelykh commented on Mar 25, 2026

    @alexey-pelykh

    Still broken on v2.1.83 (macOS) — protected-dir check short-circuits before PermissionRequest hooks in -p mode

    Version: 2.1.83
    OS: macOS (Darwin 25.3.0)

    Finding

    The protected-dir check in claude -p mode never fires a PermissionRequest event, so the hook workaround shared by @wdelhagen cannot work for non-interactive subprocess invocations.

    Evidence from stream-json output

    The model does call the Write tool. The harness intercepts it and returns an error directly — no PermissionRequest event is generated:

    // Model calls Write
    {"type":"assistant","content":[{"type":"tool_use","name":"Write","input":{"file_path":"/Users/.../.claude/.tmp/test.txt","content":"hello"}}]}
    
    // Harness returns error directly (no PermissionRequest, no hook fired)
    {"type":"user","content":[{"type":"tool_result","content":"Claude requested permissions to edit .../.claude/.tmp/test.txt which is a sensitive file.","is_error":true}]}
    
    // Final result includes permission_denials
    {"type":"result","permission_denials":[{"tool_name":"Write","file_path":".../.claude/.tmp/test.txt"}]}

    Verification

    A PermissionRequest hook with debug logging (echo "$INPUT" >> /tmp/hook-debug.log) confirms:

    • Interactive session (bypassPermissions): hook fires, log populated, write succeeds
    • claude -p (any permission mode): hook never fires, log stays empty, write blocked
    • claude -p --dangerously-skip-permissions: same — hook never fires, write blocked

    The protected-dir check runs at a layer below the PermissionRequest event system. In interactive mode it goes through PermissionRequest (hookable). In -p mode it short-circuits to an immediate error return.

    Impact

    This blocks claude -p subprocess workflows that need to write to ~/.claude/ — e.g., skill creation/editing, self-maintenance pipelines, any automation that manages Claude Code configuration.

    Suggested fix

    The protected-dir check in -p mode should go through the same PermissionRequest path as interactive mode, giving hooks a chance to allow the write. Alternatively, --dangerously-skip-permissions should bypass the protected-dir check entirely (as the flag name implies).

  16. matthew-plusprogramming commented on Mar 25, 2026

    @matthew-plusprogramming

    This is still an issue

  17. yurukusa commented on Mar 26, 2026

    @yurukusa

    @alexey-pelykh's finding is accurate and well-documented. Quick confirmation from the hooks side:
    Interactive mode: PermissionRequest hooks fire correctly for .claude/ writes. The execution order is:

    1. PreToolUse hooks run
    2. Built-in protected-directory checks run
    3. PermissionRequest hooks run (can override step 2)
    4. Tool executes
      -p mode: Step 3 never happens. The protected-dir check in step 2 returns an immediate error, bypassing the PermissionRequest event entirely. This means hook-based workarounds (like allow-claude-settings.sh or @wdelhagen's hook) only work in interactive sessions.
      Workaround for -p mode users: Currently none that I'm aware of. The --dangerously-skip-permissions flag does not bypass the protected-dir check in -p mode either (as alexey-pelykh confirmed).
      This is a core harness issue — the protected-dir check needs to go through the PermissionRequest path in all modes, not just interactive. Supporting the fix request.
  18. egalanos commented on Mar 26, 2026

    @egalanos

    and still an issue in 2.1.84 @bcherny

  19. corylanou commented on Mar 26, 2026

    @corylanou

    yeah, can confirm this bug is BACK or never truly solved. This is a huge productivity blocker for people like me that fire and forget a dozen tasks at a time only to come back and find nothing done. Seriously you guys need to learn about regression testing... these are simple things that shouldn't ever go backwards... ever... maybe teach claude that next?

  20. interconnectedMe commented on Mar 26, 2026

    @interconnectedMe

    Still broken on Claude Code VScode extension v 2.1.84

  21. interconnectedMe commented on Mar 26, 2026

    @interconnectedMe

    @bcherny — the fix from this issue didn't hold. Multiple users confirm the problem persists on v2.1.84:

    The protected directory prompt also interacts badly with the mode selector — clicking "Yes, allow all edits this session" silently downgrades the mode from Bypass to Edit Automatically, which then causes all subsequent operations to prompt.

    I've mapped the full dependency tree (12+ issues, two correlated branches, trail going back to #2933 from July 2025) in a consolidated meta-issue: #39523.

    Would appreciate this being reopened or the meta-issue being used as the tracking issue. This is a significant pain point for anyone running autonomous agent workflows.

  22. github-actions commented on Apr 3, 2026

    @github-actions

    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.

  23. locked as resolved and limited conversation to collaborators on Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions