nikivdev/code · error
Git index lock detected during merge. Remove stale .git/inde
Error message
Git index lock detected during merge. Remove stale .git/index.lock (if no git process is running) and re-run.
What it means
Thrown in src/sync.rs when `git merge --ff-only <remote_ref>` fails and its output matches `is_git_index_lock_error`. Git refuses to run because `.git/index.lock` exists, usually a stale lock left by a crashed or concurrent git process. The message instructs removing the lock only when no git process is running.
Source
Thrown at src/sync.rs:4044
recorder.record(stage, format!("already up to date with {}", remote_ref));
return Ok(());
}
sync_progressln!("Merging {} commits from {}...", behind, remote_ref);
recorder.record(
stage,
format!("merging {} commits from {}", behind, remote_ref),
);
let ff_only_output = git_run_output_in(repo_root, &["merge", "--ff-only", &remote_ref])?;
if ff_only_output.status.success() {
recorder.record(stage, format!("fast-forwarded to {}", remote_ref));
return Ok(());
}
let ff_only_detail = git_output_text(&ff_only_output);
if is_git_index_lock_error(&ff_only_detail) {
bail!(
"Git index lock detected during merge. Remove stale .git/index.lock (if no git process is running) and re-run."
);
}
if !is_expected_ff_only_merge_failure(&ff_only_detail) {
bail!(
"git merge --ff-only {} failed: {}",
remote_ref,
ff_only_detail
);
}
let merge_output = git_run_output_in(repo_root, &["merge", &remote_ref, "--no-edit"])?;
if merge_output.status.success() {
recorder.record(stage, format!("merged {} with commit", remote_ref));
return Ok(());
}
let merge_detail = git_output_text(&merge_output);
if is_git_index_lock_error(&merge_detail) {View on GitHub (pinned to a747e741ae)
Solutions
- Confirm no git process is running: `ps aux | grep git` (also check IDE git integrations).
- Remove the stale lock: `rm .git/index.lock`, then re-run sync.
- Close IDE/git GUI tools that may hold the index, then retry.
- If the lock reappears, investigate what is spawning concurrent git operations (hooks, cron jobs, parallel syncs).
Example fix
// before f sync // error: Git index lock detected during merge... // after ps aux | grep '[g]it' # verify nothing is running rm -f .git/index.lock f sync
Defensive patterns
Strategy: validation
Validate before calling
import std::path::Path;
if Path::new(".git/index.lock").exists() {
eprintln!(".git/index.lock exists; ensure no git process is running, then remove it.");
std::process::exit(1);
} Type guard
fn index_lock_stale(repo_root: &Path) -> bool {
repo_root.join(".git/index.lock").exists()
} Try / catch
match sync::run(&repo_root, &cmd) {
Err(e) if e.to_string().contains("index.lock") => {
eprintln!("Stale git index lock. Verify no git process, then: rm .git/index.lock");
}
other => other,
} Prevention
- Avoid killing git processes mid-operation; let merges/fetches finish.
- Run only one sync per working copy at a time; serialize automation jobs.
- Watch out for IDE git integrations (VS Code etc.) holding the index during sync.
- If crashes recur, clean stale locks as part of environment startup (after checking ps).
When it happens
Trigger: During sync's fast-forward stage, the ff-only merge exits with an 'Unable to create ... index.lock: File exists' style error. Commonly after a killed git process, an IDE/git GUI holding the index, or two syncs running concurrently.
Common situations: A previous sync was interrupted (Ctrl-C, crash, laptop sleep); a background IDE (VS Code git integration) holds the index; running multiple f sync instances in parallel terminals.
Related errors
- git merge --ff-only {} failed: {}
- {}. Git index stayed locked after {} retr{}; close competing
- sync agent cleared the raw stash conflicts but left the repo
- jj git export retry loop should always return
- Lin.app is not running
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/eaa07019eeaa26a6.
Report an issue: GitHub.