permissions.deny Read() rules from project settings are not enforced when launched from a subdirectory

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 5, 2026

Summary

permissions.deny Read(...) rules defined in project settings (.claude/settings.json) are enforced when the session is launched from the project root, but are silently not enforced when the session is launched from a subdirectory of the same project — even though other parts of the same settings file (e.g. enabledPlugins) demonstrably load in those sessions.

Environment

  • Claude Code v2.1.220, macOS (darwin), zsh
  • Monorepo; project settings at <repo>/.claude/settings.json (committed)
  • defaultMode: "bypassPermissions" in settings.local.json (docs state deny rules are still evaluated under bypass, and root-launch behavior confirms that)

Reproduction

<repo>/.claude/settings.json:

{
  "permissions": {
    "deny": ["Read(/build/**)"]
  }
}

A large generated tree exists at <repo>/build/** (gitignored, on disk).

  1. From the repo root: claude -p "Use the Read tool on <repo>/build/<some-file> and report the tool response verbatim"

denied: "File is in a directory that is denied by your permission settings." ✅

  1. From a subdirectory: cd <repo>/packages/backend && claude -p "<same prompt, same absolute path>"

file is read in full, no denial.

Reproduced 2×/2× in both directions. The subdirectory session does load the project settings: plugins/skills enabled only in <repo>/.claude/settings.json appear in its environment. Only the Read deny evaluation goes inert.

Pattern forms tested (all deny from root, all inert from the subdirectory)

| Deny pattern | Root launch | Subdir launch |
|---|---|---|
| Read(/build/**) (settings-source anchored per docs) | denied | allowed |
| Read(**/build/**) | denied | allowed |
| Read(/abs/path/to/repo/build/**) (absolute) | — | allowed |
| both /build/** + **/build/** together | denied | allowed |

The docs (settings reference / large-codebases page) describe leading-/ as "relative to the settings source", which reads as exactly the mechanism that should make project-level denies launch-directory-independent.

Why it matters

The large-codebases guide recommends permissions.deny Read() rules to keep agents out of generated/vendored trees (including third-party packages that ship their own CLAUDE.md/AGENTS.md agent-instruction files). In a monorepo, sessions are routinely launched from package subdirectories — precisely the sessions that end up unprotected. The failure is silent: no warning, the rule simply never matches.

Expected

Deny rules from project settings apply to any session whose project resolves to that settings file, regardless of launch cwd — or, failing that, a documented statement that deny evaluation is cwd-scoped, plus a warning when a settings-source-anchored pattern can never match from the current cwd.

View original on GitHub ↗