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

  1. Read the wrapped git error — 'pathspec did not match' means the branch is missing, 'local changes would be overwritten' means a dirty tree
  2. Run git status / git fetch in the plan directory to see the actual state
  3. Stash, reset --hard, or re-clone the plan directory to clear a dirty/corrupt tree
  4. Verify the requested branch name is correct and exists on the remote
  5. 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

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


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/0627a68e45162f63. Report an issue: GitHub.