plandex-ai/plandex · error

error removing lock files: %v

Error message

error removing lock files: %v

What it means

gitRemoveIndexLockFileIfExists removes up to three git lock files (.git/index.lock, .git/refs/heads/HEAD.lock, .git/HEAD.lock) concurrently and collects every per-goroutine error. If any removal failed (see errors 375/376/377), it aggregates them into this single wrapped error. Callers are gitAdd, gitCommit, gitCheckoutBranch, gitWriteOperation, and lockRepoDB — so a failed lock cleanup blocks the subsequent git write operation.

Source

Thrown at app/server/db/git.go:641

			}()
			if err := removeLockFile(path); err != nil {
				errCh <- err
				return
			}
			errCh <- nil
		}(path)
	}

	errs := []error{}
	for i := 0; i < len(paths); i++ {
		err := <-errCh
		if err != nil {
			errs = append(errs, err)
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("error removing lock files: %v", errs)
	}

	return nil
}

func setGitConfig(repoDir, key, value string) error {
	res, err := exec.Command("git", "-C", repoDir, "config", key, value).CombinedOutput()
	if err != nil {
		return fmt.Errorf("error setting git config %s to %s for dir: %s, err: %v, output: %s", key, value, repoDir, err, string(res))
	}
	return nil
}

func gitWriteOperation(operation func() error, repoDir, label string) error {
	log.Printf("[Git] gitWriteOperation - label: %s", label)
	var err error
	for attempt := 0; attempt < maxGitRetries; attempt++ {
		if attempt > 0 {

View on GitHub (pinned to e2d772072e)

Solutions

  1. Read the aggregated inner errors to see which path failed and why (permission vs retry-exhausted vs panic).
  2. Fix the root cause from the inner error: chmod/chown .git, free disk space, or kill the competing git process.
  3. Ensure only one writer operates on the repo — use lockRepoDB/advisory locking so concurrent app instances don't fight over locks.
  4. If a stale lock persists, remove it manually once the owning process is confirmed dead, then retry the operation.

Example fix

// before
if err := lockRepoDB(); err != nil {
    return err // opaque aggregate
}
// after: log and verify before writing
if err := lockRepoDB(); err != nil {
    log.Printf("lock cleanup failed: %v; checking for live git processes", err)
    // exec.Command("pgrep", "git")... abort if a git process is active
    return fmt.Errorf("git write aborted, lock cleanup failed: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

for _, p := range []string{"index.lock", "HEAD.lock", filepath.Join("refs", "heads", "HEAD.lock")} {
    lp := filepath.Join(repoDir, ".git", p)
    if _, err := os.Stat(lp); err == nil {
        if syscall.Access(filepath.Dir(lp), syscall.W_OK) != nil {
            return fmt.Errorf("pre-check: cannot remove %s (permissions)", lp)
        }
    }
}

Type guard

func isLockCleanupErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "error removing lock files:")
}

Try / catch

var lastErr error
for i := 0; i < 3; i++ {
    if err := gitWriteOperation(op, repoDir, "commit"); err != nil {
        if isLockCleanupErr(err) {
            time.Sleep(time.Duration(1<<i) * 200 * time.Millisecond)
            lastErr = err
            continue
        }
        return err
    }
    return nil
}
return lastErr

Prevention

When it happens

Trigger: Any gitAdd/gitCommit/gitCheckoutBranch/gitWriteOperation/lockRepoDB call where at least one lock-file goroutine returned an error: removal failure (permissions), stat failure (traversal denied), >10 removal attempts without success, or a recovered panic.

Common situations: Lock files left behind by a crashed git process combined with wrong .git ownership; repo on a full or failing disk; another long-running git process re-creating locks while this function retries (11 failed attempts); multiple app instances writing the same repo concurrently.

Related errors


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