[BUG] Launcher branch chip shows ".invalid" for repos using git's reftable backend (reads .git/HEAD directly)
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?
In a repository using git's reftable ref backend (extensions.refStorage = reftable), the launcher's current-branch chip shows .invalid instead of the actual branch (e.g. main). It persists across sessions and cache clears.
What Should Happen?
It should instead use:
git symbolic-ref --short HEAD(empty when detached → fall back togit rev-parse --short HEAD), or- parse the
# branch.headline ofgit status --porcelain=v2 --branch.
Error Messages/Logs
Steps to Reproduce
Verified from scratch (git 2.54.0):
# 1. Create a repo on the reftable backend (or convert an existing one with:
# git refs migrate --ref-format=reftable)
git init --ref-format=reftable /tmp/reftable-repro
cd /tmp/reftable-repro
git commit --allow-empty -m init # creates the default branch (main)
# 2. Confirm reftable + that .git/HEAD is the vestigial stub while git is correct
git config extensions.refStorage # -> reftable
cat .git/HEAD # -> ref: refs/heads/.invalid (the stub)
git symbolic-ref --short HEAD # -> main (git resolves correctly)
git branch --show-current # -> main
- Open this repo in the Claude launcher (the desktop/Cowork launcher branch chip).
- Observe: the branch chip shows
.invalidinstead ofmain.
Expected: branch chip shows main (matching git symbolic-ref --short HEAD).
Actual: shows .invalid (the raw .git/HEAD stub).
Claude Model
None
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.159
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
Evidence
No error is thrown — this is a silent incorrect value, so it does not appear in crash/error logs.
Verified contradiction between the raw file and git on the same repo:
| source | value |
|---|---|
| raw .git/HEAD file read | ref: refs/heads/.invalid ❌ |
| git symbolic-ref --short HEAD | main ✅ |
| git rev-parse --abbrev-ref HEAD | main ✅ |
| git branch --show-current | main ✅ |
| git status --porcelain=v2 --branch → # branch.head | main ✅ |
The launcher's own status block shows the bug directly — the current-branch line is read from the file while the "main branch" line is derived via git, so they disagree:
Current branch: .invalid
Main branch (you will usually use this for PRs): main
Status: (clean)
(Linked worktrees have the same stub at .git/worktrees/<wt>/HEAD.)
Root cause
The launcher resolves the current branch by reading the .git/HEAD file directly. Under reftable the real refs live in .git/reftable/, and .git/HEAD is a deliberate vestigial stub that git itself ignores — its contents are literally ref: refs/heads/.invalid. Any consumer that parses .git/HEAD as a file gets .invalid.
Suggested fix
Resolve the branch through git instead of reading the file:
git symbolic-ref --short HEAD(empty when detached -> fall back togit rev-parse --short HEAD), or- parse the
# branch.headline ofgit status --porcelain=v2 --branch.
The same status block's "Main branch" line is already derived correctly via git — only the current-branch line reads the file, so this is a one-spot fix.
Notes
- Likely a duplicate of #57454 (closed as duplicate; that report bundled this with a separate PR-ancestry-walker bug). Filing focused on just the reftable
.git/HEADread, since it's a clean self-contained fix. - Local stopgap (not a real fix, and git re-stamps it on the next checkout):
printf 'ref: refs/heads/main\n' > .git/HEAD.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗