JanDeDobbeleer/oh-my-posh · warning
gitstatus: reftables HEAD requires exec fallback
Error message
gitstatus: reftables HEAD requires exec fallback
What it means
resolveBranch reads HEAD directly from .git/HEAD text. With the new git 'reftable' ref-storage backend, HEAD contains the marker value 'reftablesHead' instead of a normal ref path, which this fast path cannot resolve. The error tells the caller (Load) to fall back to shelling out to git (exec) to resolve the branch.
Source
Thrown at src/gitstatus/refs.go:34
const (
branchRefPrefix = "ref: refs/heads/"
reftablesHead = "ref: refs/heads/.invalid"
)
// resolveBranch reads HEAD and resolves it to a commit hash. For a branch
// checkout it also resolves the configured upstream and, when one exists,
// the ahead/behind counts. The returned bool is false only for an unborn
// branch (HEAD points at a branch ref that has never been committed to).
func resolveBranch(opts Options, cfg *ini.File, store *objectStore, result *Result) (plumbing.Hash, bool, error) {
data, err := os.ReadFile(filepath.Join(opts.WorktreeGitDir, "HEAD"))
if err != nil {
return plumbing.ZeroHash, false, err
}
head := strings.TrimSpace(string(data))
if head == reftablesHead {
return plumbing.ZeroHash, false, errors.New("gitstatus: reftables HEAD requires exec fallback")
}
branchName, isBranch := strings.CutPrefix(head, branchRefPrefix)
if !isBranch {
return resolveDetached(head, result)
}
result.Ref = branchName
hash, ok, err := resolveRef(opts.CommonGitDir, "refs/heads/"+branchName)
if err != nil {
return plumbing.ZeroHash, false, err
}
if !ok {
// Unborn branch: no commits yet. Matches porcelain's
// `# branch.oid (initial)`. Upstream/ahead-behind are left at their
// zero values, same as the exec-git parity path for this case.View on GitHub (pinned to 0976794618)
Solutions
- Catch this error and fall back to the exec-based branch resolution (git symbolic-ref / git branch --show-current)
- If you control the repo, migrate back to files backend: git refs migrate --ref-format=files
- Ensure the fallback path in Load is wired so this error surfaces as a normal prompt render, not a hard failure
Example fix
// before
hash, isBranch, err := resolveBranch(env, result)
if err != nil { return err }
// after
hash, isBranch, err := resolveBranch(env, result)
if err != nil {
if errors.Is(err, errReftablesHead) {
return resolveBranchViaExec(env, result)
}
return err
} Defensive patterns
Strategy: fallback
Validate before calling
if data, _ := os.ReadFile(gitDir + "/HEAD"); strings.TrimSpace(string(data)) == "ref: reftable" {
// plan for exec-based resolution up front
} Type guard
func isReftablesHead(head string) bool {
return strings.TrimSpace(head) == "reftable"
} Try / catch
hash, isBranch, err := resolveBranch(env, result)
if err != nil {
if strings.Contains(err.Error(), "reftables HEAD") {
return resolveBranchViaExec(env, result)
}
return err
} Prevention
- Keep the exec fallback wired in Load at all times
- Test against repos created with git init --ref-format=reftable
- Watch for git version changes in ref-storage defaults
When it happens
Trigger: Opening a repository whose refs are stored in the reftable format (git >= 2.45 repos initialized with reftable storage, e.g. via git init --ref-format=reftable or cloned with gitoxide/Jujutsu defaults).
Common situations: Using a repo created by Jujutsu or a recent git with reftable enabled; user upgrades git or migrates refs, and the oh-my-posh gitstatus fast path stops resolving HEAD.
Related errors
- gitstatus: staged adds and deletes may be renames, deferring
- gitstatus: truncated delta insert
- gitstatus: delta target size mismatch
- git config file not found
- %s environment variable not set, do you have the posh-git mo
AI-assisted analysis of JanDeDobbeleer/oh-my-posh@0976794618 (2026-08-31).
Data as JSON: /api/errors/d34b66346397cf69.
Report an issue: GitHub.