gastownhall/beads · error
symbolic ref cycle while resolving %q
Error message
symbolic ref cycle while resolving %q
What it means
During worktree removal, bd resolves a comparison ref to its terminal (non-symbolic) ref by repeatedly running `git symbolic-ref`. This error is thrown when that resolution revisits a ref it has already seen, i.e. the symbolic ref chain forms a loop instead of terminating at a concrete ref. Git itself permits self-referential symbolic refs, so bd detects the cycle defensively and refuses to proceed.
Source
Thrown at cmd/bd/worktree_cmd.go:1909
selector: selector,
explicit: explicit,
ref: ref,
terminalRef: terminalRef,
oid: oid,
}, nil
}
func resolveWorktreeTerminalRef(
ctx context.Context,
git *worktreeRemovalGit,
executionRoot string,
ref string,
) (string, error) {
current := ref
seen := make(map[string]struct{})
for range 16 {
if _, duplicate := seen[current]; duplicate {
return "", fmt.Errorf("symbolic ref cycle while resolving %q", ref)
}
seen[current] = struct{}{}
output, err := git.output(ctx, executionRoot, "symbolic-ref", "--quiet", current)
if err == nil {
next := strings.TrimSpace(string(output))
if !strings.HasPrefix(next, "refs/") {
return "", fmt.Errorf("symbolic ref %q resolves outside refs/: %q", current, next)
}
current = next
continue
}
var exitError *exec.ExitError
if errors.As(err, &exitError) && exitError.ExitCode() == 1 {
return current, nil
}
return "", fmt.Errorf("failed to inspect symbolic ref %q: %w", current, err)
}View on GitHub (pinned to 71377f2769)
Solutions
- Run `git symbolic-ref <ref>` repeatedly on the reported ref to find the loop and break it with `git update-ref` or `git symbolic-ref <ref> <real-target>` pointing at a concrete ref
- Delete the broken ref with `git update-ref -d <ref>` (or `git branch -D`) and recreate it correctly
- Run `git fsck` to check overall repository ref/object integrity
- If only bd's comparator is affected, pass a different healthy ref via `--merged-into <ref>`
Example fix
// broken: self-referential symbolic ref $ git symbolic-ref refs/heads/feature refs/heads/feature // after: point it at a concrete target or delete it $ git symbolic-ref refs/heads/feature refs/heads/main # or $ git update-ref -d refs/heads/feature
Defensive patterns
Strategy: validation
Validate before calling
// detect a symbolic ref cycle before invoking bd
func hasRefCycle(ctx context.Context, run func(...string) ([]byte, error), ref string) bool {
seen := map[string]bool{}
cur := ref
for i := 0; i < 16; i++ {
if seen[cur] { return true }
seen[cur] = true
out, err := run("symbolic-ref", "--quiet", cur)
if err != nil { return false } // terminal ref, no cycle
cur = strings.TrimSpace(string(out))
}
return true
} Type guard
func isTerminalRef(ref string, symbolicTarget func(string) (string, bool)) bool {
_, isSymbolic := symbolicTarget(ref)
return !isSymbolic
} Try / catch
// Go: wrap the removal call and inspect the message
result, err := bd.WorktreeRemove(name)
if err != nil {
if strings.Contains(err.Error(), "symbolic ref cycle") {
// repair refs or abort
return fmt.Errorf("repair refs/heads before removing worktree: %w", err)
}
return err
} Prevention
- Never create symbolic refs pointing at themselves or at pseudo-refs like HEAD
- Run `git fsck` after scripted ref manipulation
- Prefer `git update-ref` (hard refs) over `git symbolic-ref` in automation
When it happens
Trigger: Calling worktree removal/comparator logic (`pinWorktreeComparatorRef` -> `resolveWorktreeTerminalRef`) with a ref whose `git symbolic-ref` chain loops, e.g. refs/heads/foo -> refs/heads/bar -> refs/heads/foo, or a ref pointing at itself.
Common situations: Corrupted or hand-edited .git refs; a scripted tool accidentally created a self-pointing symbolic ref (e.g. `git symbolic-ref refs/heads/x refs/heads/x`); a shallow/partial clone with damaged ref storage; filesystem-level ref manipulation gone wrong.
Related errors
- symbolic ref %q resolves outside refs/: %q
- failed to inspect symbolic ref %q: %w
- symbolic ref chain for %q exceeds 16 links
- failed to inspect created worktree cleanliness: %w %s
- created worktree is dirty after checkout; refusing to contin
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/01c6432c0f7db4c3.
Report an issue: GitHub.