Skip to content

Permission loader: a settings file that cannot exist, or holds only a BOM, fails closed #4651

Open

Description

@KuSh

With #4287, load_permission_rules adds a catch-all Ask when a Claude Code settings file exists but cannot be read or parsed, and skips missing or empty files. Two inputs fall on the wrong side of that line. Neither file holds any rules, but both get the Ask:

  • The parent path is not a directory. If a project contains a regular file named .claude, user_dirs::project_root still accepts it as the marker, because it only checks .exists(). The same happens if CLAUDE_CONFIG_DIR points at a file. Reading <that>/settings.json then fails with ENOTDIR (ErrorKind::NotADirectory), not NotFound, so it is treated as unreadable. Windows reports NotFound for the same layout and skips the file, so the result depends on the platform.
  • The file holds only a UTF-8 BOM. For example, a settings.local.json saved empty by an editor that writes EF BB BF. content.trim() does not remove U+FEFF, so the empty-file check misses it. from_json_str then strips the BOM, fails on the empty input, and the file counts as unparseable.

Measured with rtk rewrite "git status", where the other settings file has allow: ["Bash(git status)"]:

input develop e542dca0b develop + #4287
project .claude is a regular file 0 3, failed to read permissions … Not a directory (os error 20) (twice)
settings.local.json = EF BB BF 0, parse warning 3, parse warning

Under RTK_REWRITE_HOST=openclaw, a command no rule matches also goes from 0 to 3 in both cases.

What would close it:

  • treat NotADirectory like NotFound (stable since Rust 1.83; the MSRV is 1.91);
  • run the empty check after strip_leading_bom.

A test for each that goes through load_permission_rules_from.

Activity

  1. rtk-wshm-sync-bot commented on Oct 18, 2026

    @rtk-wshm-sync-bot

    wshm · Automated triage by AI

    🔍 Automated Triage

    🐛 Category bug
    🟡 Priority medium
    🎯 Confidence 93%

    Summary

    Claude Code permission loader fails closed (adds catch-all Ask) for a non-directory .claude path or a BOM-only settings file instead of treating them as absent/empty, with platform-divergent behavior.

    This looks like a simple fix that could be auto-resolved. Repo maintainers can use /wshm fix to attempt it.

    📁 Relevant files
    • src/permissions.rs
    • src/user_dirs.rs

    Suggested Actions

    1. Treat io::ErrorKind::NotADirectory like NotFound when reading settings files (MSRV 1.91, stable since 1.83)
    2. Run the empty-content check after strip_leading_bom in load_permission_rules_from
    3. Add regression tests: .claude as a regular file, and a BOM-only settings.local.json
    4. Cross-reference Cursor, Gemini and Droid permission loaders drop an unreadable or unparseable settings file's rules #4652 (loss of rules in other agent loaders) for consistent fail-open/closed semantics across loaders

    Triaged automatically by wshm · This is an automated analysis, not a human review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions