[BUG] Marketplace plugin re-materialization mid-session cleans the pinned hook installPath, silently killing hooks in every running session
[BUG] Marketplace plugin re-materialization mid-session cleans the pinned hook installPath, silently killing hooks in every running session
Summary
When a marketplace plugin is updated mid-session (new version bump + marketplace re-materialization), a running session's hooks are pinned to the previous version's install path. The re-materialization removes that old path, so the pinned hook commands become unreachable and silently stop firing — no error, no warning, and the session otherwise runs normally. Every session that was live at the moment of re-materialization loses that plugin's hooks at once. The only recovery is /reload-plugins (if the user happens to know to run it) or a full restart; nothing surfaces that hooks have died.
This is the mid-session sibling of the boot-time race in #63028, and it is distinct from the already-closed issues (see "Why this is not a duplicate" below).
Environment
- Claude Code CLI, marketplace-installed plugin (git-backed marketplace)
- Plugin declares hooks in
hooks/hooks.json(Notification, PreToolUse, PostToolUse, PermissionDenied) - Many long-running sessions across a tmux fleet
What the docs say (this is the pinned-path mechanism)
The Plugins reference documents the pinning directly:
"When a plugin updates mid-session, hook commands, monitors, MCP servers, and LSP servers keep using the previous version's path. Run /reload-plugins to switch hooks, MCP servers, and LSP servers to the new path; monitors require a session restart."
So hooks are snapshotted at session start and bound to that version's install path. The failure is what happens when the old path is removed by re-materialization while a session is still bound to it: the pinned command path no longer exists, and the hook silently no-ops.
Steps to reproduce
- Install a marketplace plugin that registers PreToolUse/PostToolUse/Notification hooks.
- Start several Claude Code sessions; confirm hooks fire (e.g. the plugin writes a per-invocation debug log).
- Bump the plugin's
versioninplugin.jsonand push to the marketplace so Claude Code re-materializes the plugin cache dir (replacing/removing the old versioned path). - In the already-running sessions, keep working (keep hitting tool calls / permission prompts).
Expected: hooks continue firing (from either the old or the new path), OR the user is told the plugin was updated and hooks need reloading.
Actual: the running sessions' hooks silently stop firing entirely. No error is printed, nothing in the UI changes, and the sessions keep working — the user only discovers it when a permission prompt or notification that should have fired doesn't. /reload-plugins recovers it, but there is no signal that it's needed.
Concrete real-world repro (observed)
A plugin (claude-tmux-attention, a tmux notification plugin) was bumped from 0.2.4 and re-materialized mid-day. Its debug log records every hook invocation with a session id. Sweeping the log afterward: ~6 sessions that were demonstrably alive (their operators were actively conversing with them, transcripts growing) each logged a last hook event and then went totally silent for 13,000–17,000 subsequent log lines while other sessions kept logging thousands. There is a clean cliff — no session's last-event lands in the gap — consistent with a single re-materialization event dropping hooks for every session live at that instant. Survivors are sessions started after the event. Restart (or /reload-plugins) is the only recovery.
Why this is not a duplicate of existing issues
- #55867 ("Mid-session hook configuration reload") was closed as duplicate. It covers the general "no mid-session reload" gap for settings.json + hook-script bodies; it does not track the specific re-materialization-removes-pinned-installPath → silent total hook death across live sessions, and it produced no fix.
- #52218 ("Plugin autoUpdate doesn't update installed_plugins.json, hooks pinned to stale installPath") is scoped to
autoUpdaterewritinginstalled_plugins.json. This report is broader: it happens on ordinary marketplace re-materialization after a version bump, and the killer is the old path being cleaned away, not just a stale pointer to a still-present path. - #63028 (declared plugins inactive on first session) and #10997 / #11509 (SessionStart hooks miss first run) are boot-time races. This is the mid-session variant.
- #64699 ("Hooks stop firing entirely after editing settings.local.json") is open and the same class (a mid-session config event silently kills hooks; restart doesn't always recover) — related, different trigger.
Requested fix (either would resolve it)
- Auto-re-register hooks to the new path on re-materialization / plugin update, so a live session self-heals instead of pointing at a deleted path — or, at minimum, keep the old path readable until sessions bound to it end.
- A hook-health introspection surface so the silent failure becomes detectable. The #64699 commenter's proposal is a good shape: after any reload/re-materialization, expose per-plugin config hash, accepted/rejected hook count with reasons, active phases, and a last-fire timestamp per event — plus a
claude-doctor-style check ("N sessions have stale/dead hook registration; run/reload-pluginsor restart"). Even a loud warning on the affected session would be a large improvement over the current silent death.
A detection surface alone (option 2) would be enough to turn this from a silent data-loss-class failure (missed permission prompts / notifications) into a loud, actionable one.
Related
- #63028 (boot-time inactive plugins) — sibling race, open
- #64699 (hooks die after settings.local.json edit) — same class, open
- #55867 (closed as dup), #52218 (autoUpdate-scoped), #56631 (reload incomplete), #10997 / #11509 (first-run SessionStart)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗