[Bug] Orphaned plugins execute hooks despite being uninstalled

Resolved 💬 4 comments Opened Mar 21, 2026 by jordan-of Closed Mar 24, 2026

Bug Description
Bug Report: Orphaned Plugins Execute Hooks Despite Being Uninstalled

From: Jordan Dea-Mattson
GitHub: @jordan-of (OrdinaryFolk), @jordandm (personal)
Email: jordan-of@users.noreply.github.com
Related: #35587 (hookify "hook error" messages), #35575 (disabled plugins register hooks)

Problem

Claude Code's plugin system allows orphaned, uninstalled plugins to continue executing hooks. This produces persistent "hook error" messages on every session start and every prompt submission,
with no indication of which plugin is causing the errors or how to fix them.

Root Cause (Confirmed)

The semgrep plugin was in the following state:

  • Enabled in .claude/settings.json → enabledPlugins → "semgrep@claude-plugins-official": true
  • Orphaned — had .orphaned_at marker file at ~/.claude/plugins/cache/claude-plugins-official/semgrep/0.5.1/.orphaned_at
  • Not installed — absent from ~/.claude/plugins/installed_plugins.json
  • Binary missing — semgrep not installed on the machine
  • Hooks cached — hooks.json still registered handlers for SessionStart, UserPromptSubmit, and PostToolUse

The hook executor resolved hooks from the cached hooks.json, attempted to run semgrep mcp -k inject-secure-defaults (and two other commands), all of which failed with exit code 127 (command not
found). Claude Code displayed these as "hook error" with no detail about which hook or why.

Impact

  • Three error messages per session: two on SessionStart:startup, one on every UserPromptSubmit
  • No diagnostic information: the error message is just "hook error" — no hook name, no command, no exit code, no stderr
  • Desensitization: persistent false errors train users to ignore the error line, masking real hook failures
  • Hard to diagnose: took significant investigation to trace from "hook error" → hookify (wrong) → semgrep plugin (correct) → orphaned state → cached hooks → missing binary

The Bug (Three Separate Failures)

  1. Orphaned plugins should not execute hooks.

The .orphaned_at marker exists for a reason — the plugin system should check it before resolving a plugin's hooks.json. If a plugin is orphaned, its hooks should be deregistered or skipped.

  1. enabledPlugins is not pruned when a plugin is uninstalled.

The plugin was removed from installed_plugins.json but remained in enabledPlugins in settings.json. Either uninstallation should remove the entry, or the hook executor should cross-reference
installed_plugins.json before trusting enabledPlugins.

  1. Hook error messages contain no diagnostic information.

"hook error" tells the user nothing. At minimum, the message should include:

  • The hook name or plugin name
  • The command that was attempted
  • The exit code
  • A truncated stderr

Example: SessionStart:startup hook error: semgrep mcp -k inject-secure-defaults (exit 127: command not found) would have made this a 30-second fix instead of an hour of investigation.

Fix Applied

Manually removed "semgrep@claude-plugins-official": true from enabledPlugins in settings.json and deleted the orphaned cache directory. All three hook errors disappeared immediately.

Recommendation

  1. The hook executor should check both enabledPlugins AND installed_plugins.json AND the absence of .orphaned_at before executing a plugin's hooks
  2. Plugin uninstallation should remove the enabledPlugins entry
  3. Hook error messages should include the failing command and exit code
  4. Consider a /plugins doctor command that detects orphaned-but-enabled plugins

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.1.81
  • Feedback ID: a4a44346-ad55-4369-949b-e1aaad4ee7ab

Errors

[{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/jordan_of/.local/share/claude/versions/2.1.81 (expected in multi-process scenarios)\n    at TET (/$bunfs/root/src/entrypoints/cli.js:2724:2174)\n    at fgq (/$bunfs/root/src/entrypoints/cli.js:2724:1318)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-03-21T06:27:37.768Z"}]

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗