security-guidance plugin hook fails with ENOENT in local-agent-mode-sessions staging, causing infinite retry/notification loop

Status Open
Maintainer reply None cached
Activity 4 comments · opened Sep 7, 2026

Summary

The security-guidance (v2.0.7, official plugin) hook security_reminder_hook.py intermittently fails with ENOENT when invoked from its per-session staging path, and the failure retries indefinitely rather than surfacing once — flooding the session with repeated "Idle." / "(no change)" notification turns and making the session effectively unusable. Restarting the Claude Code desktop app does not clear the loop.

Environment

  • Windows 11 Home Single Language 10.0.26200
  • Claude Code desktop app, autoUpdatesChannel: "latest"
  • Plugin: security-guidance@claude-plugins-official, marketplace claude-plugins-official, cached version 2.0.7
  • Plugin id referenced in the failing path: plugin_01YBNfaNwQztYsnUydt8m47G

Exact error (repeated, verbatim)

C:\Users\<user>\AppData\Local\Python\pythoncore-3.14-64\python.exe: can't open file 'C:\Users\<user>\AppData\Roaming\Claude\local-agent-mode-sessions\<session-id>\<subsession-id>\rpm\plugin_01YBNfaNwQztYsnUydt8m47G\hooks\security_reminder_hook.py': [Errno 2] No such file or directory

Delivered repeatedly as background <task-notification> events (summary: "Background security review found issues"), each turn, across multiple concurrent sessions on the same project simultaneously.

What we verified before filing

  1. The plugin's real install is intact: ~/.claude/plugins/cache/claude-plugins-official/security-guidance/2.0.7/hooks/security_reminder_hook.py exists and is well-formed.
  2. The per-session staged manifest (.../rpm/manifest.json) correctly lists plugin_01YBNfaNwQztYsnUydt8m47G as security-guidance.
  3. At the moment we checked, the staged copy at the exact failing path (.../rpm/plugin_01YBNfaNwQztYsnUydt8m47G/hooks/security_reminder_hook.py) did exist, fully staged, matching the source file — yet the same session kept throwing the identical ENOENT error on the next several turns regardless. This points to a race condition in how/when the hook is invoked relative to per-session plugin staging, not a corrupted or missing install.
  4. Restarting the Claude Code app three times did not stop the loop (the rpm/ staging folder was confirmed regenerated fresh each time, same failure reproduced immediately).
  5. It affected multiple concurrent session windows on the same project at once, all independently stuck in the same repeat pattern (some reaching 45+ repeats), each showing a "stop" control in its own window but no way to prevent the loop from resuming.

Workaround that resolved it

Disabling / removing the security-guidance plugin (Settings → Plugins, or setting "security-guidance@claude-plugins-official": false in ~/.claude/settings.json) followed by an app restart stopped the loop for good. No other plugin was involved.

Suggested fix direction

  • Whatever triggers security_reminder_hook.py per-turn should tolerate the plugin's per-session rpm/ staging not yet being complete (retry-with-backoff or wait-for-stage, rather than immediate hard failure), and should not re-fire on every subsequent turn once it has already failed for that turn.
  • The failure should not present to the user as a synthetic <task-notification> "Background security review found issues" — this framing made a plugin infrastructure bug look like an actual security finding, and it repeated across every turn/session, effectively making the session unusable and requiring a plugin uninstall to work around.

View original on GitHub ↗

3 Comments

AlasdairSavage · 1 month ago

Corroborating this from a separate environment — same plugin ID (plugin_01YBNfaNwQztYsnUydt8m47G), same hook (security_reminder_hook.py), same infinite-retry-no-backoff pattern, same misleading "Background security review found issues" notification framing.

Differences from the original report, likely just environment variance rather than a different bug: our Python interpreter was the Microsoft Store app-execution-alias stub (AppData\Local\Microsoft\WindowsApps\python3.13.exe) rather than a pythoncore install, and our local ~/.claude/settings.json / per-session rpm/manifest.json referenced marketplace name knowledge-work-plugins rather than claude-plugins-official for the same plugin ID.

One additional data point: we saw the hook fire as a synchronous PostToolUse:Edit hook failure (not just the periodic background notification), immediately after an Edit tool call — so it's not confined to whatever triggers the periodic "security review" notification; it's wired into normal tool-call hook dispatch too.

We also independently reproduced the fix: adding explicit "security-guidance@<marketplace>": false entries to enabledPlugins in ~/.claude/settings.json, followed by a restart, per the original report's workaround.

AlasdairSavage · 1 month ago

Update from our side: we've now tried every local-config lever suggested here, in order —

"security-guidance@...": false in enabledPlugins — did not stop the loop
Fully removing the enabledPlugins entries entirely — did not stop the loop
Fully killing all claude.exe processes via Task Manager, a full system reboot, and a fresh app start — the identical ENOENT/"Background security review found issues" loop is still firing afterward

This rules out local settings.json state and even a full process/OS restart as a fix, and reinforces the earlier point that the failing per-session rpm/ staging path isn't something a client-side restart clears. At this point it looks like it'd need a fix shipped by Anthropic rather than anything actionable locally.

7487 · 23 days ago

Related reports: #95727 and #94634.

I reproduced a narrower failure in the public launcher: sg-python.sh executes Python directly, so a missing script produces interpreter exit 2, although no review ran. The Stop hook enables asyncRewake, whose documented wake-up condition is exit 2. Its fixed summary says “Background security review found issues”. The Python stop_hook_active guard cannot run when that script is missing.

Minimal reproduction from the repository root, with Python 3 installed:

sg_repro_dir=$(mktemp -d)
bash plugins/security-guidance/hooks/sg-python.sh "$sg_repro_dir/missing.py" </dev/null
echo "$?" # 2
rmdir "$sg_repro_dir"

A minimal mitigation is a script-path existence check before interpreter dispatch:

if [ "$#" -gt 0 ] && [ "${1#-}" = "$1" ] && [ ! -e "$1" ]; then
    echo "security-guidance: hook script not found: $1" >&2
    exit 1
fi

All six current hooks.json invocations pass a script path first. This preserves interpreter-option passthrough and directory execution; it does not remap legitimate review exit 2.

In a temporary copy on macOS, ten controlled comparisons passed: missing script, script exits 0/1/2, two -c cases, -m, -, no-argument stdin, and directory execution. Paths containing spaces were included. Only the missing-script case changed (2→1); the controlled finding stub retained exit 2, and existing outputs were unchanged. No security scan or model call ran.

This validates the launcher mitigation, not the full Desktop loop. Windows/Git Bash remain untested. It does not repair staging, address replayed notifications, or eliminate the check-to-exec race.

Analysis and local validation were prepared with AI assistance.

Showing cached comments. Read the full discussion on GitHub ↗