[BUG]Permissions system requires internal tool names (TaskStop) that are not discoverable; invalid names fail silently

Status Fixed / completed
Reported on v2.1.158
Maintainer reply None cached
Activity 4 comments · opened Jun 15, 2026 · closed Jun 15, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The permissions system requires users to know opaque internal tool names (e.g., TaskStop), but provides no way to discover them. Users who enter incorrect or partial names (e.g., "Stop Task", "stop", "kill-task") get no error, no warning, no suggestion — the rule simply does nothing.
Concretely: /permissions presents human-readable display names like "Stop Task" in its UI, but the underlying runtime matches deny rules against internal function names like TaskStop. A user adding a deny rule for "Stop Task" via the UI will see the rule saved — but it will never fire, because the display name does not match the internal name.

What Should Happen?

The core problem is: the permission system’s input surface is unvalidated and opaque. Users cannot discover valid tool identifiers, and invalid input fails silently. Any fix should address at least the minimum tier:
Minimum (validation):
When a deny rule matches no known tool name, the system should surface an error and list all valid tool identifiers. A developer writing "stop" naturally expects fuzzy matching to TaskStop — currently they get silent failure.
Better (discovery):
Provide a --list-tools flag or API endpoint so users and scripts can dynamically retrieve the canonical tool identifier list.
Ideal (ergonomics):
Support fuzzy matching, prefix matching, or accept the human-readable display names and description fields as aliases for tool identification.

Error Messages/Logs

Steps to Reproduce

  1. Run /permissions → add a deny rule for "Stop Task".
  2. Verify the rule appears in settings.
  3. Ask Claude to stop a background task.
  4. Claude successfully calls TaskStop — the deny rule does not block it.
  5. Run /permissions again → add a deny rule for "TaskStop" (the internal name, discovered by trial‑and‑error).
  6. Now TaskStop is correctly blocked.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.158

Platform

Other

Operating System

Windows

Terminal/Shell

Windows Terminal

Additional Information

This is not an isolated confusion. Other developers have assumed natural names like "stop" would map to TaskStop because the UI shows "Stop Task". The silent failure caused a real user loss: a 40‑minute cold‑start process was killed because the developer thought they had blocked "stop" but no error or fallback existed.
The /permissions UI already knows the human‑readable names — it should either accept those as aliases or at least warn when a rule is added that will never match.

View original on GitHub ↗

4 Comments

yurukusa · 4 months ago

Confirming this from the matching side, and adding a discovery method that works today.
Why the silent failure happens. Permission rules match against the canonical tool identifier (the name field in the tool-use protocol, e.g. TaskStop, Bash, Read), with no alias table and no fuzzy/prefix matching. The /permissions UI shows the human-readable label ("Stop Task"), but the runtime only ever compares your rule string to the canonical name. So a Stop Task deny rule is saved, validates as well-formed, and silently never fires — exactly your repro. There is no place in the flow where "this rule will never match any known tool" is detected, so I agree the missing validation tier is the real bug.
Discovery that already works (no trial-and-error needed). The canonical names the matcher uses are written to your session transcript. Every time a tool runs, the JSONL records a tool_use block whose .name is the exact identifier deny matches against. So you can recover the correct string by running the tool once, then grepping the transcript:

grep -ho '"type":"tool_use"[^}]*"name":"[^"]*"' \
  ~/.claude/projects/*/*.jsonl | grep -o '"name":"[^"]*"' | sort -u

On my machine this returns the literal strings (Bash, Read, Edit, TaskStop, AskUserQuestion, Skill, …) — i.e. the values a deny rule has to match character-for-character. That turns "guess until it fires" into "look it up". A --list-tools flag would still be the right fix, but the data is already there in the logs.
For the destructive-loss case specifically (the 40-min cold start that got killed): the dangerous property is that a UI-label rule fails silently — you only learn it didn't bind after the loss. A PreToolUse hook is keyed on the same canonical name programmatically, so besides being able to block (exit 2), it can also log every time it fires and every time it sees a tool it doesn't recognize — which makes a mis-keyed rule visible before it costs you something, instead of after. A few lines in settings.json:

{ "hooks": { "PreToolUse": [
  { "matcher": "TaskStop",
    "hooks": [ { "type": "command",
      "command": "echo \"[blocked TaskStop] $(date)\" >> ~/.claude/deny.log; exit 2" } ] }
]}}

Because the matcher runs on the canonical tool name (the same string from the transcript), there's no display-name mismatch, and the log line tells you it actually engaged.

github-actions[bot] · 4 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/29694
  2. https://github.com/anthropics/claude-code/issues/57091

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

Bastetfriend · 4 months ago

Thanks for closing this and confirming the bug!

One follow-up: could you confirm whether the /permissions dialog and/or the permissions docs page will be updated alongside the fix? The worry is that even after the code fix ships, future users will still face the same silent-failure pitfall unless:

  • The /permissions UI shows canonical tool names (or accepts display names as aliases), OR
  • The docs page provides a discoverable list of valid tool identifiers.

A grep-on-JSONL workaround is useful for power users, but the average user who sees "Stop Task" in the UI should not need to mine session transcripts to write a working deny rule.

github-actions[bot] · 1 month 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.