plandex-ai/plandex · error
error checking out git branch for dir: %s, err: %v, output:
Error message
error checking out git branch for dir: %s, err: %v, output: %s
What it means
If the requested branch differs from the current one, gitCheckoutBranch runs `git -C repoDir checkout <branch>` and, on failure, wraps both the exec error and git's combined stdout/stderr output in this message. The included git output is the key diagnostic — it usually says exactly why the checkout was refused (unknown branch, dirty conflicts, lock files).
Source
Thrown at app/server/db/git.go:455
cmd := exec.Command("git", "-C", repoDir, "branch", "--show-current")
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return fmt.Errorf("error getting current git branch for dir: %s, err: %v", repoDir, err)
}
currentBranch := strings.TrimSpace(out.String())
log.Printf("[Git] gitCheckoutBranch - currentBranch: %s", currentBranch)
if currentBranch == branch {
log.Printf("[Git] gitCheckoutBranch - already on branch %s, skipping", branch)
return nil
}
log.Println("[Git] gitCheckoutBranch - checking out branch:", branch)
res, err := exec.Command("git", "-C", repoDir, "checkout", branch).CombinedOutput()
if err != nil {
return fmt.Errorf("error checking out git branch for dir: %s, err: %v, output: %s", repoDir, err, string(res))
}
return nil
}
func gitRewindToSha(repoDir, sha string) error {
res, err := exec.Command("git", "-C", repoDir, "reset", "--hard", sha).CombinedOutput()
if err != nil {
return fmt.Errorf("error executing git reset for dir: %s, sha: %s, err: %v, output: %s", repoDir, sha, err, string(res))
}
return nil
}
func getLatestCommit(dir string) (sha, body string, err error) {
var out bytes.Buffer
cmd := exec.Command("git", "log", "--pretty=%h@@|@@%at@@|@@%B@>>>@")
cmd.Dir = dir
cmd.Stdout = &outView on GitHub (pinned to e2d772072e)
Solutions
- Read the `output:` portion of the message — it contains git's own reason (e.g. "pathspec 'X' did not match any file(s) known to git").
- If the branch is missing, create it first (`git checkout -b <branch>`) or verify the branch name spelling against `git branch --list`.
- Remove a stale .git/index.lock after confirming no git process is running, then retry the checkout.
- Serialize concurrent checkouts with a repo-level lock (lockRepoDB) and ensure working-tree cleanliness (`git status --porcelain`) before switching branches.
Example fix
// before
res, err := exec.Command("git", "-C", repoDir, "checkout", branch).CombinedOutput()
// after (create-if-missing fallback)
res, err := exec.Command("git", "-C", repoDir, "checkout", branch).CombinedOutput()
if err != nil {
if strings.Contains(string(res), "did not match") {
res2, err2 := exec.Command("git", "-C", repoDir, "checkout", "-b", branch).CombinedOutput()
if err2 != nil {
return fmt.Errorf("error checking out git branch for dir: %s, err: %v, output: %s", repoDir, err2, string(res2))
}
return nil
}
return fmt.Errorf("error checking out git branch for dir: %s, err: %v, output: %s", repoDir, err, string(res))
} Defensive patterns
Strategy: fallback
Validate before calling
out, _ := exec.Command("git", "-C", repoDir, "branch", "--list", branch).Output()
branchExists := len(strings.Fields(string(out))) > 0
if !branchExists {
// create it: git checkout -b <branch>
}
if dirty, _ := exec.Command("git", "-C", repoDir, "status", "--porcelain").Output(); len(dirty) > 0 {
// stash or commit before switching branches
} Try / catch
if err := gitCheckoutBranch(repoDir, branch); err != nil {
if strings.Contains(err.Error(), "error checking out git branch") {
log.Printf("checkout failed for %s in %s; git said: %s", branch, repoDir, extractGitOutput(err))
// fallback: create branch if missing, clear stale index.lock, or abort cleanly
}
} Prevention
- Always read the git `output:` embedded in the error — it names the exact refusal reason.
- Create plan branches explicitly at plan creation instead of assuming they exist.
- Serialize all checkouts on a repo with a lock to avoid racing index files.
- Keep the working tree clean (commit/stash) before branch switches.
- Remove stale .git/index.lock only when no git process is running.
When it happens
Trigger: Checking out a branch that does not exist locally (never created), a corrupted or locked repo (index.lock), untracked/modified files conflicting with the target branch, or a bare/invalid repo.
Common situations: Requesting a plan branch that was never committed to; interrupted prior git operation leaving index.lock; concurrent processes checking out branches in the same repo without locking; repo on read-only mount.
Related errors
- error resetting file %s: %v
- error getting current git branch for dir: %s, err: %v
- error checking out branch: %v
- Error deleting branch:
- error getting git root: %s
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/584fdb2e8e19078d.
Report an issue: GitHub.