plandex-ai/plandex · error

error removing lock file: %v

Error message

error removing lock file: %v

What it means

removeLockFile (app/server/db/git.go:557) removes stale git lock files (index.lock, HEAD.lock) before write operations. This error is returned when os.Remove on the lock file fails for a reason other than the file not existing — e.g. permission denied, the path being a non-empty directory, or the file being held in use on Windows. It aborts the lock-cleanup retry loop immediately rather than retrying, since retries won't fix a filesystem-level refusal.

Source

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

	if err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("error checking lock file: %v", err)
	}

	attempts := 0
	for exists {
		if attempts > 10 {
			return fmt.Errorf("error removing index.lock file: %v after %d attempts", err, attempts)
		}

		log.Printf("[Git] removeLockFile - removing index.lock file: %s, attempt: %d", lockFilePath, attempts)

		if err := os.Remove(lockFilePath); err != nil {
			if os.IsNotExist(err) {
				log.Printf("[Git] removeLockFile - %s file not found, skipping removal", lockFilePath)
				return nil
			}

			return fmt.Errorf("error removing lock file: %v", err)
		}

		_, err = os.Stat(lockFilePath)
		exists = err == nil

		if err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("error checking lock file: %v", err)
		}

		log.Printf("[Git] removeLockFile - after removal, %s file exists: %t", lockFilePath, exists)
		if exists {
			log.Printf("[Git] removeLockFile - %s file still exists, retrying after delay", lockFilePath)
		} else {
			log.Printf("[Git] removeLockFile - %s file removed successfully", lockFilePath)
			return nil
		}

		attempts++

View on GitHub (pinned to e2d772072e)

Solutions

  1. Fix filesystem permissions on the .git directory: chown -R the app user or chmod u+w on the lock file and parent dir.
  2. Check the wrapped %v error: EACCES/EPERM -> permissions, EBUSY -> another process holds it, stop competing processes.
  3. If the repo dir is on a read-only mount, remount read-write or relocate the repo.
  4. As a last resort for a truly stale lock, delete it manually with elevated privileges, then re-run the operation.

Example fix

// before
if err := os.Remove(lockFilePath); err != nil {
    return fmt.Errorf("error removing lock file: %v", err)
}
// after: check writability before calling git operations
if info, err := os.Stat(repoDir + "/.git"); err == nil && info.Mode().Perm()&0200 == 0 {
    os.Chmod(repoDir+"/.git", 0755) // or chown to the service user
}
Defensive patterns

Strategy: try-catch

Validate before calling

lp := filepath.Join(repoDir, ".git", "index.lock")
if fi, err := os.Stat(lp); err == nil {
    if err := syscall.Access(filepath.Dir(lp), syscall.W_OK); err != nil {
        return fmt.Errorf("cannot remove %s: no write permission on %s", lp, filepath.Dir(lp))
    }
    _ = fi
}

Type guard

func isRemovableLockErr(err error) bool {
    return err != nil && !os.IsNotExist(err)
}

Try / catch

err := gitCommit(repoDir, msg)
if err != nil && strings.Contains(err.Error(), "error removing lock file") {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {
        return fmt.Errorf("fix .git ownership: chown -R %s %s/.git", user, repoDir)
    }
    return err
}

Prevention

When it happens

Trigger: Called via gitRemoveIndexLockFileIfExists from gitAdd, gitCommit, gitCheckoutBranch, gitWriteOperation, or lockRepoDB when a lock file exists and os.Remove fails with a non-ENOENT error (e.g. EACCES, EPERM, EISDIR).

Common situations: Repo .git directory owned by a different user (repo cloned with sudo, app running as another user); read-only mounted volume; Windows antivirus or another process briefly holding the file; permissions broken by a container image copy.

Related errors


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