[BUG] Desktop app's embedded git sync re-fetches all remote tags, bypassing `remote.<name>.tagOpt --no-tags`
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
While the Claude desktop app is open on a repository, an in-process (embedded) git client — apparently the cloud-session ref sync (fetchOutcomeBranches / [runner:session] in the binary) — periodically fetches all remote tags, ignoring the repository's remote.origin.tagOpt --no-tags setting.
Our monorepo's origin carries ~65,000 CI-generated tags (per-PR builder/checker merge tags). The sync re-creates all of them as loose ref files on every app refresh cycle (startup / window focus), landing ~30-45 s after the app's visible git.exe background fetch. 65k loose refs under .git/refs/tags/ make every ref enumeration crawl on Windows/NTFS (a 20-second git log --tags, sluggish git GUIs), and any manual cleanup is silently undone within the hour.
The user cannot work around it:
- There is no setting or environment variable to opt out of background fetching, in the app or in the docs.
remote.origin.tagOpt --no-tagsis ignored (the embedded client does not consult git config).- No
git.exeis spawned, so wrappers, hooks (core.hooksPath), and credential-helper interception never see it. - The only mitigation we found is a filesystem ACL denying file creation under
.git/refs/tags/.
Note: the other background fetch the app runs (worktree/default-branch freshness) is well-behaved — it spawns git.exe, names the remote, and so respects tagOpt. Only the embedded sync misbehaves.
What Should Happen?
Preferred: a setting to opt out of all background fetches (per repository or globally) — both the embedded session-ref sync and the spawned default-branch freshness fetch. Some teams have policies against unattended fetches, and Claude Code currently offers no way to comply: the app fetches on its own schedule regardless of user intent, and features that depend on a fetch could simply fall back to the locally cached refs (as the worktree documentation already describes for the failure case).
Independently of the opt-out, the tag behavior is a bug in its own right: the embedded sync should fetch only the refs it needs and skip tags (the equivalent of --no-tags), matching the app's own spawned fetch path, which already passes --no-tags --no-recurse-submodules --no-write-fetch-head --no-auto-gc — or at minimum respect remote.<name>.tagOpt.
Error Messages/Logs
There are no error messages — the sync is silent. Forensic traces instead:
Tag floods leave **no reflog entries, no FETCH_HEAD write, and no new packs**; ~65k loose ref files are written over ~60 s (per-ref writes, not git's batched ref transaction):
$ ls --time-style=full-iso -lt .git/refs/tags/ # one flood event
drwxr-xr-x ... 2026-09-07 14:30:03.574 release
drwxr-xr-x ... 2026-09-07 14:30:01.382 release-candidate
drwxr-xr-x ... 2026-09-07 14:29:01.183 prbuilder-merge <- ~62 s of ref writing
A WMI process-creation watcher on `git.exe` captured every git process during multiple flood events. The only background fetch ever spawned is the (innocent) default-branch freshness fetch:
git.exe -c core.hooksPath=\\.\NUL -c safe.directory=* -c core.fsmonitor=false
-c branch.autoSetupMerge=false -c diff.ignoreSubmodules=dirty -c diff.submodule=short
-c status.submoduleSummary=false -c submodule.recurse=false -c gc.auto=0 -c maintenance.auto=false
fetch --no-recurse-submodules --no-write-fetch-head origin +refs/heads/master:refs/remotes/origin/master
Canary timing (repeated 3x): delete one tag -> app focus event -> spawned git.exe fetch at T+0 -> canary re-appears at T+30..45 s with no git.exe activity in between (e.g. focus fetch 16:17:04, canary ref file mtime 16:17:36).
Steps to Reproduce
- Clone a repo whose remote has a very large number of tags (tens of thousands; any repo with per-build CI tags works). Full clone, tags present.
git config remote.origin.tagOpt --no-tags- Delete the local tags (
git for-each-ref --format="delete %(refname)" refs/tags | git update-ref --stdin), thengit pack-refs --all. - Open the Claude desktop app on this repo (a session associated with the repo; we observed it with cloud/session features untouched at defaults).
- Give the app window focus and wait ~1 minute.
git for-each-ref refs/tags | wc -l— all remote tags are back as loose refs, despitetagOpt. Control: replaying the spawnedgit.exe fetchcommand from step 5's process listing verbatim does not restore them; only the app's embedded sync does.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
1.46388.1.0
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
- Claude desktop app 1.46388.1.0 (Windows Store) with Claude Code CLI 2.1.260; also observed with the 2.1.25x line since ~Sep 4. The August line (~2.1.22x) only ran the well-behaved spawned fetch and caused no floods once
tagOptwas set. - Windows 11 Pro 10.0.26200, Git for Windows 2.52.0.windows.1.
- Strings in
claude.exeassociated with the suspected code path:fetchOutcomeBranches,[runner:session] fetched N outcome branch(es),CLAUDE_CODE_SYNC_SESSION_REFS. - The canary tag used points at a CI merge commit not reachable from the default branch, so ordinary tag auto-follow of a branch fetch cannot explain its return — the sync fetches tags wholesale.
- Impact scales with org size: CI tag storms of this magnitude are common in large monorepos, and the flood re-applies after every cleanup, degrading all local git tooling.