{"record":{"id":"01c6432c0f7db4c3","repo":"gastownhall/beads","slug":"symbolic-ref-cycle-while-resolving-q","errorCode":null,"errorMessage":"symbolic ref cycle while resolving %q","messagePattern":"symbolic ref cycle while resolving %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/worktree_cmd.go","lineNumber":1909,"sourceCode":"\t\tselector:    selector,\n\t\texplicit:    explicit,\n\t\tref:         ref,\n\t\tterminalRef: terminalRef,\n\t\toid:         oid,\n\t}, nil\n}\n\nfunc resolveWorktreeTerminalRef(\n\tctx context.Context,\n\tgit *worktreeRemovalGit,\n\texecutionRoot string,\n\tref string,\n) (string, error) {\n\tcurrent := ref\n\tseen := make(map[string]struct{})\n\tfor range 16 {\n\t\tif _, duplicate := seen[current]; duplicate {\n\t\t\treturn \"\", fmt.Errorf(\"symbolic ref cycle while resolving %q\", ref)\n\t\t}\n\t\tseen[current] = struct{}{}\n\n\t\toutput, err := git.output(ctx, executionRoot, \"symbolic-ref\", \"--quiet\", current)\n\t\tif err == nil {\n\t\t\tnext := strings.TrimSpace(string(output))\n\t\t\tif !strings.HasPrefix(next, \"refs/\") {\n\t\t\t\treturn \"\", fmt.Errorf(\"symbolic ref %q resolves outside refs/: %q\", current, next)\n\t\t\t}\n\t\t\tcurrent = next\n\t\t\tcontinue\n\t\t}\n\t\tvar exitError *exec.ExitError\n\t\tif errors.As(err, &exitError) && exitError.ExitCode() == 1 {\n\t\t\treturn current, nil\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"failed to inspect symbolic ref %q: %w\", current, err)\n\t}","sourceCodeStart":1891,"sourceCodeEnd":1927,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/worktree_cmd.go#L1891-L1927","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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>`"],"exampleFix":"// broken: self-referential symbolic ref\n$ git symbolic-ref refs/heads/feature refs/heads/feature\n// after: point it at a concrete target or delete it\n$ git symbolic-ref refs/heads/feature refs/heads/main\n# or\n$ git update-ref -d refs/heads/feature","handlingStrategy":"validation","validationCode":"// detect a symbolic ref cycle before invoking bd\nfunc hasRefCycle(ctx context.Context, run func(...string) ([]byte, error), ref string) bool {\n\tseen := map[string]bool{}\n\tcur := ref\n\tfor i := 0; i < 16; i++ {\n\t\tif seen[cur] { return true }\n\t\tseen[cur] = true\n\t\tout, err := run(\"symbolic-ref\", \"--quiet\", cur)\n\t\tif err != nil { return false } // terminal ref, no cycle\n\t\tcur = strings.TrimSpace(string(out))\n\t}\n\treturn true\n}","typeGuard":"func isTerminalRef(ref string, symbolicTarget func(string) (string, bool)) bool {\n\t_, isSymbolic := symbolicTarget(ref)\n\treturn !isSymbolic\n}","tryCatchPattern":"// Go: wrap the removal call and inspect the message\nresult, err := bd.WorktreeRemove(name)\nif err != nil {\n\tif strings.Contains(err.Error(), \"symbolic ref cycle\") {\n\t\t// repair refs or abort\n\t\treturn fmt.Errorf(\"repair refs/heads before removing worktree: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["git","symbolic-ref","ref-cycle","worktree"],"backgroundTag":"git-symbolic-ref-cycle","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}