plandex-ai/plandex · error
error checking out branch: %v
Error message
error checking out branch: %v
What it means
If the lock request specified a branch, lockRepoDB runs gitCheckoutBranch on the plan directory after acquiring the DB lock; failure yields 'error checking out branch'. The repo lock is held (lock id returned), but the working tree is not on the requested branch. The git error is wrapped in the message.
Source
Thrown at app/server/db/locks.go:472
}
}
}()
// check if git lock file exists
// remove it if so
err = gitRemoveIndexLockFileIfExists(getPlanDir(orgId, planId))
if err != nil {
log.Printf("[Lock] %s | %s | Error removing lock file: %v", planId, params.Reason, err)
return newLock.Id, fmt.Errorf("error removing lock file: %v", err)
}
if branch != "" {
// checkout the branch
err = gitCheckoutBranch(getPlanDir(orgId, planId), branch)
if err != nil {
log.Printf("[Lock] %s | %s | Error checking out branch: %v", planId, params.Reason, err)
return newLock.Id, fmt.Errorf("error checking out branch: %v", err)
}
log.Printf("[Lock] %s | %s | Checked out branch", planId, params.Reason)
}
return newLock.Id, nil
}
func deleteRepoLockDB(id, planId, reason string, numRetry int) error {
start := time.Now()
goroutineID := getGoroutineID()
if locksVerboseLogging {
log.Printf("[Lock][Delete][%d] START delete lock %s at %v | reason: %s", goroutineID, id, start, reason)
defer func() {
log.Printf("[Lock][Delete][%d] END delete lock took %v | reason: %s", goroutineID, time.Since(start), reason)
}()
}View on GitHub (pinned to e2d772072e)
Solutions
- Read the wrapped git error — 'pathspec did not match' means the branch is missing, 'local changes would be overwritten' means a dirty tree
- Run git status / git fetch in the plan directory to see the actual state
- Stash, reset --hard, or re-clone the plan directory to clear a dirty/corrupt tree
- Verify the requested branch name is correct and exists on the remote
- Release the lock id returned with the error if you do not proceed
Example fix
// before
id, err := lockRepoDB(ctx, orgId, planId, reason)
if err != nil { return err }
// after
id, err := lockRepoDB(ctx, orgId, planId, reason)
if err != nil {
if strings.Contains(err.Error(), "error checking out branch") && id != "" {
deleteRepoLockDB(id, planId, "branch checkout failed", 0)
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
cmd := exec.Command("git", "-C", planDir, "rev-parse", "--verify", "refs/heads/"+branch)
if err := cmd.Run(); err != nil {
return fmt.Errorf("branch %s does not exist in %s", branch, planDir)
} Type guard
func branchExists(planDir, branch string) bool {
return exec.Command("git", "-C", planDir, "rev-parse", "--verify", "refs/heads/"+branch).Run() == nil
} Try / catch
id, err := lockRepoDB(ctx, orgId, planId, reason)
if err != nil {
if strings.Contains(err.Error(), "error checking out branch") && id != "" {
_ = deleteRepoLockDB(id, planId, "checkout failed", 0)
}
return err
} Prevention
- Verify the branch exists (git fetch + rev-parse) before requesting the lock
- Keep plan working trees clean (reset/stash after each build)
- Avoid shallow clones if arbitrary branch checkout is needed
- Release the returned lock id when aborting on checkout failure
When it happens
Trigger: gitCheckoutBranch fails: branch does not exist locally/remotely, uncommitted or untracked changes would be overwritten, detached HEAD conflicts, or the repo has a corrupt index.
Common situations: Requesting a branch that was force-deleted on the remote; leftover dirty working tree from a previously crashed build; branch name typo or case mismatch; repo shallow-cloned without the target ref.
Related errors
- Error deleting branch:
- error getting current git branch for dir: %s, err: %v
- error checking out git branch for dir: %s, err: %v, output:
- error removing lock file: %v
- Error getting branches:
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/0627a68e45162f63.
Report an issue: GitHub.