Plugin auto-update creates incomplete cache directory, blocking session hooks
Bug description
When Claude Code auto-updates monorepo plugins (e.g., claude-plugins-official), it creates a new version directory in the plugin cache but does not always copy hook script files to the new directory. CC then resolves $CLAUDE_PLUGIN_ROOT to the newest directory (by mtime) instead of the version listed in installed_plugins.json, causing all hooks to fail with [Errno 2] No such file or directory.
This is session-blocking — when a UserPromptSubmit hook fails this way, the user cannot send any messages. The only fix is manually copying files from the old directory to the new one.
Reproduction steps
- Have
hookifyplugin installed at version27d2b86d72da - Wait for CC to auto-update the monorepo — it creates
~/.claude/plugins/cache/claude-plugins-official/hookify/2cd88e7947b7/ - The new directory is created with partial structure —
hooks/directory exists but Python files (stop.py, userpromptsubmit.py, etc.) are missing installed_plugins.jsonstill saysversion: "27d2b86d72da"(old, working version)- CC resolves
$CLAUDE_PLUGIN_ROOTto2cd88e7947b7(the new, incomplete directory) - All hooks fail:
python3: can't open file '/home/user/.claude/plugins/cache/claude-plugins-official/hookify/2cd88e7947b7/hooks/stop.py': [Errno 2] No such file or directory
UserPromptSubmithook blocks — user cannot interact with Claude
Evidence
# installed_plugins.json says old version:
hookify: installed=27d2b86d72da updated=2026-01-30
# But CC created new dir and resolves CLAUDE_PLUGIN_ROOT there:
~/.claude/plugins/cache/claude-plugins-official/hookify/2cd88e7947b7/ (Birth: 2026-03-16 23:47)
# Old dir has all files:
27d2b86d72da/hooks/stop.py ✓ exists (Jan 29)
27d2b86d72da/hooks/userpromptsubmit.py ✓ exists (Jan 29)
# New dir was missing them:
2cd88e7947b7/hooks/stop.py ✗ missing until manual copy
2cd88e7947b7/hooks/userpromptsubmit.py ✗ missing until manual copy
Same issue affected security-guidance plugin (also has hooks/ directory).
6 other monorepo plugins had the same version divergence (installed_plugins.json says 27d2b86d72da, cache has 2cd88e7947b7) but were not affected because they don't have hook scripts.
Root causes (two compounding issues)
- Non-atomic plugin update: CC creates the new version directory with partial content. Hook script files are not always included in the initial sync.
- Version resolution mismatch: CC resolves
$CLAUDE_PLUGIN_ROOTto the newest directory in the cache (by mtime), not the version declared ininstalled_plugins.json. This means a half-written update directory immediately becomes the active plugin root.
Impact
- Session-blocking: UserPromptSubmit hook failure prevents all user interaction
- Silent: No warning before the session breaks — user discovers it when they can't type
- Non-technical users: The only fix (copying files between cache directories) requires terminal knowledge the average user doesn't have
- Broad surface: Any plugin with hooks/ scripts is at risk during monorepo auto-updates
Suggested fix
$CLAUDE_PLUGIN_ROOTshould resolve to the version declared ininstalled_plugins.json, not the newest cache directory- Plugin updates should be atomic: only switch the active version after all files are confirmed present
- Hook failures should never block
UserPromptSubmit— degrade gracefully instead of preventing interaction
Environment
- Claude Code on WSL2 (Ubuntu), Linux 6.6.87.2-microsoft-standard-WSL2
- Plugins: hookify@claude-plugins-official, security-guidance@claude-plugins-official
- Plugin cache:
~/.claude/plugins/cache/claude-plugins-official/
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗