Plugin 'Update now' / writes metadata without refreshing marketplace source (gh-76882 + gh-83422 combined root cause)
Bug
claude plugin update / interactive "Update now" calls updatePluginOp (src/services/plugins/pluginOperations.ts) which resolves the plugin entry via getPluginById → getPluginByIdCacheOnly — a cache-only fast path that reads the local marketplace clone without touching the network. There is no refreshMarketplace step before version calculation, so the update computes the new version from stale marketplace data.
Reproduction
- Install a plugin from a third-party github marketplace (e.g.
code-wiki@code-wiki, source{ "source": "github", "repo": "ye-c/code-wiki" }) - Push a new version (bump
plugin.jsonversion) to the remote - Run
claude plugin update code-wiki@code-wiki(or click "Update now" in/pluginUI) - Restart Claude Code
Expected: Plugin updated to the new version, /code-wiki skill available.
Actual: installed_plugins.json shows the new version/installPath/lastUpdated/gitCommitSha, but the cache directory at ~/.claude/plugins/cache/{marketplace}/{plugin}/{version}/ is missing or carries stale content. The plugin's skills/commands are silently absent — invoking /code-wiki gets "unknown skill".
Root cause
updatePluginOp (src/services/plugins/pluginOperations.ts) resolves the plugin entry via getPluginById (src/utils/plugins/marketplaceManager.ts), which first tries getPluginByIdCacheOnly — reading only the local marketplace clone. No refreshMarketplace is called beforehand, so:
- For local-source plugins: the entire update uses the stale marketplace clone's
entry.source/entry.version. - For remote (github) plugins:
cachePluginre-clones independently (so the SHA is fresh), butentry.versionfrom the stale marketplace clone can still mis-version the cache path.
This is the combined root cause of #76882 (marketplace update fetches new version but doesn't update installed_plugins.json) and #83422 (autoUpdate refreshes marketplace clone but never updates existing installs). The interactive "Update now" button hits the same path via handleSingleOperation('update') in src/commands/plugin/ManagePlugins.tsx.
Workaround
Uninstall + reinstall (bypasses the update path entirely, rebuilds cache + metadata):
claude plugin uninstall <plugin>@<marketplace>
claude plugin install <plugin>@<marketplace>
Fix
Refresh the marketplace source at the top of updatePluginOp before getPluginById, so version calculation uses current data:
// src/services/plugins/pluginOperations.ts, in updatePluginOp
if (marketplaceName) {
try {
await refreshMarketplace(marketplaceName)
} catch (error) {
logForDebugging(`updatePluginOp: marketplace refresh failed for ${marketplaceName}: ${errorMessage(error)}`)
}
}
refreshMarketplace already short-circuits for settings-sourced and seed-managed marketplaces (src/utils/plugins/marketplaceManager.ts), so the added call is a no-op for those. Failure is non-fatal — a transient refresh error shouldn't block updating from whatever source is available.
Environment
- Claude Code 2.1.212, macOS (darwin 25.6.0)
- Third-party github marketplace, user scope
from cc @ claude-bro
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗