Plugin autoUpdate doesn't update installed_plugins.json, leaving bundled hooks pinned to stale installPath

Resolved 💬 6 comments Opened Apr 23, 2026 by Gilssung Closed May 31, 2026

Summary

When a plugin marketplace has "autoUpdate": true in ~/.claude/plugins/known_marketplaces.json, Claude Code promotes the plugin's runtime version at process start (shown in /plugin UI, used for skills/commands) — but does not update ~/.claude/plugins/installed_plugins.json. Because plugin-bundled hooks load from the installPath recorded in that file, autoUpdate can never deliver a new bundled hook to users; they're silently pinned to whatever version was last explicitly installed.

A second ordering issue compounds this: autoUpdate appears to run before SessionStart hooks during process startup, so a hook that git pulls the marketplace cache only primes the cache for the next relaunch. Each plugin release ends up needing two relaunches to fully propagate.

Environment

  • Claude Code: v2.1.117
  • Platform: macOS (Darwin 25.2.0)
  • Plugin: custom enterprise plugin bundling hooks/hooks.json (SessionStart, matcher-wrapper schema)

Reproduction

  1. Create a plugin with "autoUpdate": true in its marketplace entry and a bundled hooks/hooks.json whose SessionStart command writes a timestamp to a log file (so you can observe firings).
  2. /plugin install <name>installed_plugins.json records version: A, installPath: .../A/.
  3. Push version B to the plugin repo with a clearly different hook body.
  4. Fully quit and relaunch Claude Code (no manual /plugin marketplace update or /plugin update).
  5. Observe:
  • /plugin UI shows version B (autoUpdate advanced skills/commands) ✅
  • installed_plugins.json still says A (installPath still .../A/) ❌
  • $CLAUDE_PLUGIN_ROOT inside the hook log resolves to .../A/ — i.e., A's hook is still firing, not B's.

Expected behavior

When autoUpdate promotes a plugin to a newer version, installed_plugins.json should also be rewritten so version and installPath reflect the newer version. Bundled hooks should load from the new installPath.

Actual behavior

installed_plugins.json is only updated by explicit user actions:

  • /plugin install
  • /plugin update
  • /plugin UI → Update now

autoUpdate hot-loads newer skills/commands into the running process but leaves installed_plugins.json untouched, pinning bundled hooks to the last-explicitly-installed version.

Impact

  • Bundled hook updates never reach users via autoUpdate. Plugin authors who want to iterate on their hook must instruct every user to click Update now manually for each hook change.
  • Every release takes two relaunches to propagate, even when the hook logic is stable, because autoUpdate runs before the SessionStart hook during process start. The hook's fetch only primes the cache for the next relaunch's autoUpdate.
  • For plugins with simple stable hooks (e.g., a boring git pull) this is a latent gotcha rather than a breaking bug. For plugins that want to evolve their hooks, it's a blocker.

Suggested fixes

Either or both would resolve this:

  1. Persist autoUpdate results. When autoUpdate loads a newer version at process start, atomically rewrite installed_plugins.json (version + installPath), and subsequently load bundled hooks from the new installPath.
  2. Reorder startup. Run SessionStart hooks before the autoUpdate version-resolution step, so a hook that primes the marketplace cache can influence the same relaunch's autoUpdate decision.

Workaround (current)

Users must run /plugin UI → Update now once after any plugin release that changes the bundled hook. After that, simpler bundled hooks (e.g., git pull to keep the marketplace cache fresh) keep working across subsequent autoUpdate cycles as long as the hook command itself doesn't need to evolve.

View original on GitHub ↗

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