[BUG] Desktop: personal git-marketplace plugins never auto-update despite autoUpdate:true; Update button is a silent no-op; stale gitCommitSha after CLI update
[BUG] Desktop: personal git-marketplace plugins never auto-update despite autoUpdate: true; Update button produces no logged action; stale gitCommitSha after CLI update
Environment
- Claude Desktop (macOS, Darwin 25.x): app 1.5354.0
- Claude Code CLI: 2.1.121
- Plugin: personal private GitHub marketplace (
TheMikeFactoryMustGrow/drive-manager-plugin, nestedplugins/<name>/layout, marketplace.json at repo root)
Summary
A personal git-marketplace plugin sat at v2.2.0 in Desktop for ~3 weeks while its repo advanced 2.2.1 → 2.6.0, despite "autoUpdate": true on the marketplace entry (set 2026-06-09). The Desktop plugin panel's Update button produced no plugin-manager log activity and no visible result or error. The terminal CLI updated the same plugin immediately.
Evidence (from ~/.claude/plugins/* and ~/Library/Logs/Claude/main.log)
- autoUpdate never fired.
known_marketplaces.jsonfor this marketplace shows"autoUpdate": truebutlastUpdatedjumped directly from the last manual operation to2026-07-02T13:22:43Z— the moment a human ranclaude plugin marketplace updatein a terminal. No unattended refresh in between, across ~3 weeks and multiple app restarts. - Update button: no logged action. During the session where the panel showed v2.2.0 and Update was clicked,
main.logcontains zeroCCDMarketplacePluginManagerCLIupdate entries. The only "Checking for updates" lines are the Electron app[updater](app self-update), not plugins. - Historical Desktop-side failure on the same plugin class (2026-05-04, install path, logged):
````
[CCDMarketplacePluginManagerCLI] Failed to install plugin: Installing plugin "<plugin>@other"...
✘ Failed to install plugin "<plugin>@other": This plugin uses a source type your
Claude Code version does not support. Update Claude Code and try again.
The same operation via terminal CLI 2.1.x succeeded — the Desktop-embedded manager and the terminal CLI behave differently on git-source marketplaces.
- Metadata bug after CLI update:
installed_plugins.jsonnow shows"version": "2.6.0"with"gitCommitSha": "63515eb..."— a commit from 2026-05-04 (many releases earlier). The update flow doesn't refreshgitCommitSha. - CLI output oddity:
claude plugin updateprinted "updated from 2.2.0 to 2.4.1" while the resulting install record says 2.6.0 — suggests the version string in the success message is computed from a pre-refresh snapshot.
Expected
- With
autoUpdate: true, the marketplace clone refreshes and the plugin updates unattended (or on app start). - The Update button triggers the same code path as
claude plugin marketplace update+claude plugin update, and surfaces failures to the user. gitCommitShareflects the installed version's commit.
Actual
- No unattended updates ever occur for personal git marketplaces.
- Update button is a silent no-op (no log entry, no error toast, version unchanged).
- Metadata desyncs after successful CLI updates.
Workaround
Terminal CLI: claude plugin marketplace update <marketplace> then claude plugin update <plugin>@<marketplace>, then restart the app.
Prior reports (all closed, symptom persists in adjacent form)
#38185 (Personal tab fails to load / can't update), #38429 (sync removes 3rd-party marketplace plugins), #39274 (marketplace installs removed as NOT_AVAILABLE), #40600 (personal marketplace install lost on restart). Today's evidence is specifically about the update path + autoUpdate flag, which those didn't cover.
3 Comments
Confirming this on a much newer build, and I think I can narrow the mechanism: the app never attempts a fetch. It isn't a fetch that fails.
Environment
1.24012.9karlkfi/claude-pr-sentinel, public repo, SSH remote,marketplace.jsonat repo rootautoUpdate: true, set at install on 2026-07-26Worth noting the public/SSH combination, since #73673 is a private repo — this isn't private-repo-specific.
Controlled test
I cut a release of my own plugin mid-session, so the timeline is exact:
| Event | Local time |
| --- | --- |
| v0.5.0 commit on remote
main| 09:29:35 || v0.5.0 release published | 09:40:37 |
| Follow-up PR merged (
32e238a) | 10:07:16 || Claude.app process start | 10:11:20 |
| Checked state, still
0.4.1| 13:10 |Full app restart four minutes after the remote advanced,
autoUpdate: true, upstream two commits ahead. Three hours later the installed version is still0.4.1andinstalled_plugins.jsonstill pinsgitCommitSha: b7dac8dfrom install day.The clone has never fetched, provably
The marketplace clone has
logallrefupdates = true, so any ref update leaves a reflog entry. There is exactly one, from the original clone:No
FETCH_HEADat all.git fetchwrites that file on every invocation, including a no-op fetch that finds nothing new, so its absence means no fetch has ever run against this clone.What the app does touch at startup
Exactly one thing under
.git, 48 seconds after launch:.git/indexis the stat cache — that's agit status-shaped read, not a fetch. So the update path does reach the clone, looks at it, and stops.No git-update calls in the log at all
Across the whole of
main.log:[LocalPluginsReader] Found 5 local pluginsfires at startup and that is the end of it.RemotePluginManagerruns its periodic sync every 600000ms, but it is only doing account-scoped plugins —fetchBrowsableRemotePlugins: 92 plugins,fetchAccountScopedRemotePlugins: 0 plugins from 1 marketplace(s), where that one marketplace is the Anthropic service, not any of my five git ones. Every mention of my marketplace names in the log is the workspace repo (sessions, PR checks, worktree creation), never the clone under~/.claude/plugins/marketplaces/.Not an auth or transport problem
The same app, same session, fetches the same repo over the same SSH remote without trouble:
So git works, SSH works, that remote works. The marketplace clone is simply never asked.
The clones are shallow, but that turns out not to matter
--depth 1, pinned at the install commit, single-branch refspec. So the clone genuinely cannot see the new release until something fetches — but I want to correct my own first read of this: I initially assumed a fix would also need--unshallow. It does not. I ran the CLI workaround afterward and it fetched straight into the shallow clone and advanced it to the new HEAD, still shallow. Git just moves the shallow boundary. Shallowness is a red herring here; the only defect is the absent fetch.The CLI workaround works, and reproduces the stale
gitCommitShaInstalling the CLI and running the two documented commands fixed it immediately, on the same machine where the app had done nothing for three hours:
FETCH_HEADexists now and the clone sits at the new HEAD, which is the cleanest possible control: same clone, same remote, same machine, same credentials. The CLI can do it; the app never asks.That also reproduces point 4 of the original report. After a successful update:
b7dac8dis the v0.4.1 commit. The clone is at32e238aand the version string is correct, butgitCommitShawas never refreshed — so that half of this issue is not private-repo-specific or app-version-specific either.Suggested framing
The reported symptom is "autoUpdate doesn't work", but the narrower defect looks like: Desktop has no code path that refreshes a git-backed marketplace clone.
autoUpdate: trueis persisted intoknown_marketplaces.jsonand then nothing consumes it —known_marketplaces.json's ownlastUpdatedis still frozen at install time here, matching what this issue already reported. That would also explain the silent no-op Update button: there is nothing to invoke.So I read this as two separable defects, and the first looks much cheaper to fix than the second:
gitCommitSha, in the CLI as well as Desktop.Happy to run anything else against this machine. The plugin repo is public if a reproduction against a real marketplace is useful.
(Edited to correct my claim that a fix would need
--unshallow— it does not — and to add the CLI-workaround result and thegitCommitShareproduction.)Confirming point 4 specifically (the stale
gitCommitShahalf, separate from the Desktop autoUpdate mechanism above), and narrowing it: it is not Desktop-specific and not specific to personal/private marketplaces. It reproduces via the plain CLI against the official marketplace, and the discriminator appears to be the plugin's install shape.The tell needs no repo access — the entry contradicts itself
superpowers@claude-plugins-official, straight out of~/.claude/plugins/installed_plugins.json:The entry claims
6.2.0, butplugin.jsonat that commit says5.0.7:The plugin really was updated — the cache dir at
installPathgenuinely contains 6.2.0, andlastUpdatedmoved to July — soversion,installPathandlastUpdatedall refreshed whilegitCommitShastayed at the install-time commit. Same on a third-party marketplace (nova@nova-marketplace): entry says1.14.0, itsgitCommitShais a1.0.0-era commit frominstalledAt, 22 commits back.It correlates with install shape, not with the plugin
Every plugin installed on one machine, each recorded SHA checked against its source repo's
main:| marketplace entry's
source| plugins |gitCommitSha||---|---|---|
| the marketplace repo itself (relative path) | 5 plugins | matches
origin/mainexactly — 5/5, moves on every update ||
{"source": "url", "url": "..."}— a separate repo |superpowers,nova| frozen at install — 2/2 |Both drifting plugins are
source.url; both were updated well afterinstalledAt; neither SHA moved. The 5 marketplace-hosted ones update correctly on the same machine, same CLI, same day — so this isn't a general "updates don't refresh metadata" problem.Worth noting the manifest often already has the right answer:
claude-plugins-official's entry for superpowers pinssource.sha: 44c9b2d…, which is currentmain— so the marketplace knows the installed commit while the registry records a different one.Why it matters
installed_plugins.jsonis the natural place for tooling to answer "is this plugin current?". Forsource.urlplugins that check is silently and permanently wrong: comparing the recorded SHA to the repo'smainreports "stale" on an install that is fully up to date, and no user action clears it. I hit this building a freshness check and had to fall back to comparing theversionin the cache dir's ownplugin.jsonagainst the source repo — with a regression test pinning the reason, because the SHA approach is the one that looks correct.Happy to be told the field is only ever meant to record the install-time commit — in which case the bug is narrower (the entry's own
versionandgitCommitShadescribing different releases) and it's mostly a documentation gap. Either way the current behaviour is a trap for anyone reading the registry.Original filer, checking back in 8 weeks after filing. Still reproducing on current builds, and I can corroborate both confirmations above from a second machine.
Desktop 1.32352.1: still zero fetch activity
Same test @karlkfi ran, on a build ~8,300 numbers newer than theirs (1.24012.9 → 1.32352.1; CLI 2.1.220):
No marketplace mentions of any kind in the log. A month of Desktop releases, no change.
Cleanest evidence yet: unserviced marketplaces just freeze
I run a launchd workaround (the CLI two-command loop from the original report) that services only my
git-source marketplaces. My threegithub-source marketplaces haveautoUpdate: trueand nothing servicing them, which makes them clean probes. Theirknown_marketplaces.json→lastUpdated, as of 2026-08-28:| marketplace | lastUpdated | frozen for |
|---|---|---|
| process-design-plugin | 2026-07-02 | 56 days |
| claude-plugins-official | 2026-07-25 | 33 days |
| linglepedia-create-note | 2026-07-25 | 33 days |
(The two July-25 stamps are from a manual CLI run; nothing native has ever moved any of them. Note
claude-plugins-officialis the official marketplace — this isn't a personal-marketplace edge case.)A verification trap worth flagging
While checking whether native auto-update had started firing, I found a marketplace
lastUpdatedthat had advanced with no LaunchAgent run to explain it — which looks exactly like the fix landing. It wasn't: a headless CLI session had started two seconds earlier and re-cloned the marketplace at startup (the clone's.git/logs/HEADshowed a freshclone:entry, not a fetch). SolastUpdatedadvancing is not sufficient evidence of native auto-update; attribution needs the reflog plus knowing what else ran at that timestamp. @karlkfi'sFETCH_HEADtell remains the reliable discriminator.Release notes through 2.1.241
Scanned the CHANGELOG for both defects: plenty of marketplace work (refresh-before-install at
/plugin install, GitLab sources,headersHelper, the known_marketplaces.json concurrent-write fix) but nothing touching either one —gitCommitShadoesn't appear in it at all.The CLI workaround continues to work perfectly on a 6-hour schedule, which keeps pointing at the framing above: the refresh logic exists and works; Desktop never invokes it.