[BUG] Desktop: personal git-marketplace plugins never auto-update despite autoUpdate:true; Update button is a silent no-op; stale gitCommitSha after CLI update

Status Open
Reported on v2.1.121
Maintainer reply None cached
Activity 3 comments · opened Jul 3, 2026

[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, nested plugins/<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)

  1. autoUpdate never fired. known_marketplaces.json for this marketplace shows "autoUpdate": true but lastUpdated jumped directly from the last manual operation to 2026-07-02T13:22:43Z — the moment a human ran claude plugin marketplace update in a terminal. No unattended refresh in between, across ~3 weeks and multiple app restarts.
  2. Update button: no logged action. During the session where the panel showed v2.2.0 and Update was clicked, main.log contains zero CCDMarketplacePluginManagerCLI update entries. The only "Checking for updates" lines are the Electron app [updater] (app self-update), not plugins.
  3. 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.

  1. Metadata bug after CLI update: installed_plugins.json now shows "version": "2.6.0" with "gitCommitSha": "63515eb..." — a commit from 2026-05-04 (many releases earlier). The update flow doesn't refresh gitCommitSha.
  2. CLI output oddity: claude plugin update printed "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.
  • gitCommitSha reflects 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.

View original on GitHub ↗

3 Comments

karlkfi · 1 month ago

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

  • Claude Desktop macOS (Darwin 25.5.0), app 1.24012.9
  • Marketplace: karlkfi/claude-pr-sentinel, public repo, SSH remote, marketplace.json at repo root
  • autoUpdate: true, set at install on 2026-07-26

Worth 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 still 0.4.1 and installed_plugins.json still pins gitCommitSha: b7dac8d from 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:

$ cat ~/.claude/plugins/marketplaces/pr-sentinel/.git/logs/HEAD
0000000000000000000000000000000000000000 b7dac8dbcdd5... clone: from github.com:karlkfi/claude-pr-sentinel.git

$ ls ~/.claude/plugins/marketplaces/pr-sentinel/.git/FETCH_HEAD
No such file or directory

$ ls ~/.claude/plugins/marketplaces/pr-sentinel/.git/logs/refs/remotes/origin/main
No such file or directory

No FETCH_HEAD at all. git fetch writes 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:

$ find .git -newermt '2026-07-27 00:00'
.git
.git/index

.git/index is the stat cache — that's a git 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:

$ grep -icE "git fetch|git clone|git ls-remote|updateMarketplace|refreshMarketplace" ~/Library/Logs/Claude/main.log
0

[LocalPluginsReader] Found 5 local plugins fires at startup and that is the end of it. RemotePluginManager runs 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:

2026-07-27 09:16:02 [info] [createWorktree] full origin fetch completed in 1265ms { baseRepo: '/Users/karl/workspace/claude-pr-sentinel' }

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

$ git -C ~/.claude/plugins/marketplaces/pr-sentinel rev-parse --is-shallow-repository
true
$ cat .git/shallow
b7dac8dbcdd531875dab5ee04fa9dbbb9156744e
$ git cat-file -t a2b8057
fatal: Not a valid object name a2b8057

--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 gitCommitSha

Installing the CLI and running the two documented commands fixed it immediately, on the same machine where the app had done nothing for three hours:

$ claude plugin marketplace update pr-sentinel
✔ Successfully updated marketplace: pr-sentinel

$ claude plugin update pr-sentinel@pr-sentinel
✔ Plugin "pr-sentinel" updated from 0.4.1 to 0.5.0 for scope user. Restart to apply changes.

FETCH_HEAD exists 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:

version = 0.5.0 | sha = b7dac8d | lastUpdated = 2026-07-27T20:43:51.782Z

b7dac8d is the v0.4.1 commit. The clone is at 32e238a and the version string is correct, but gitCommitSha was 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: true is persisted into known_marketplaces.json and then nothing consumes it — known_marketplaces.json's own lastUpdated is 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:

  1. Desktop never invokes the marketplace refresh that the CLI clearly already implements correctly.
  2. The update path does not write 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 the gitCommitSha reproduction.)

jjackson · 28 days ago

Confirming point 4 specifically (the stale gitCommitSha half, 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:

{
  "installPath": "~/.claude/plugins/cache/claude-plugins-official/superpowers/6.2.0",
  "version": "6.2.0",
  "installedAt": "2026-05-01T18:35:24.068Z",
  "lastUpdated": "2026-07-24T18:41:54.433Z",
  "gitCommitSha": "e7a2d16476bf042e9add4699c9d018a90f86e4a6"
}

The entry claims 6.2.0, but plugin.json at that commit says 5.0.7:

$ git show e7a2d16:.claude-plugin/plugin.json | jq -r .version
5.0.7
$ git rev-list --count e7a2d16..origin/main
241

The plugin really was updated — the cache dir at installPath genuinely contains 6.2.0, and lastUpdated moved to July — so version, installPath and lastUpdated all refreshed while gitCommitSha stayed at the install-time commit. Same on a third-party marketplace (nova@nova-marketplace): entry says 1.14.0, its gitCommitSha is a 1.0.0-era commit from installedAt, 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/main exactly — 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 after installedAt; 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 pins source.sha: 44c9b2d…, which is current main — so the marketplace knows the installed commit while the registry records a different one.

Why it matters

installed_plugins.json is the natural place for tooling to answer "is this plugin current?". For source.url plugins that check is silently and permanently wrong: comparing the recorded SHA to the repo's main reports "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 the version in the cache dir's own plugin.json against 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 version and gitCommitSha describing different releases) and it's mostly a documentation gap. Either way the current behaviour is a trap for anyone reading the registry.

TheMikeFactoryMustGrow · 2 days ago

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):

$ grep -icE "git fetch|git clone|git ls-remote|updateMarketplace|refreshMarketplace" ~/Library/Logs/Claude/main.log
0

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 three github-source marketplaces have autoUpdate: true and nothing servicing them, which makes them clean probes. Their known_marketplaces.jsonlastUpdated, 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-official is 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 lastUpdated that 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/HEAD showed a fresh clone: entry, not a fetch). So lastUpdated advancing is not sufficient evidence of native auto-update; attribution needs the reflog plus knowing what else ran at that timestamp. @karlkfi's FETCH_HEAD tell 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 — gitCommitSha doesn'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.