nikivdev/code · error
{}. Git index stayed locked after {} retr{}; close competing
Error message
{}. Git index stayed locked after {} retr{}; close competing git/jj processes or remove stale .git/index.lock, then retry. What it means
Raised in src/sync.rs by the `jj git export` retry wrapper when the command keeps failing because the git index is locked (.git/index.lock held by another process). After exhausting all configured retry delays (JJ_GIT_EXPORT_LOCK_RETRY_DELAYS_MS), the library bails with the jj failure message plus guidance to close competing git/jj processes or remove a stale lock, then retry. It exists to surface a persistent lock contention problem rather than looping forever.
Source
Thrown at src/sync.rs:4643
"jj git export hit Git index lock; retrying {retry_num}/{max_retries} in {}ms",
delay.as_millis()
),
);
sync_progressln!(
"jj git export hit a transient Git index lock; retrying ({}/{}) in {}ms...",
retry_num,
max_retries,
delay.as_millis()
);
std::thread::sleep(delay);
continue;
}
jj_print_output(&output);
let failure = jj_failure_message(&args, &output);
if is_git_index_lock_error(&failure_text) {
let retries = JJ_GIT_EXPORT_LOCK_RETRY_DELAYS_MS.len();
bail!(
"{}. Git index stayed locked after {} retr{}; close competing git/jj processes or remove stale .git/index.lock, then retry.",
failure,
retries,
if retries == 1 { "y" } else { "ies" }
);
}
bail!("{}", failure);
}
unreachable!("jj git export retry loop should always return");
}
fn jj_run_in(repo_root: &Path, args: &[&str]) -> Result<()> {
let output = jj_run_output_in(repo_root, args)?;
jj_print_output(&output);
if !output.status.success() {
bail!("{}", jj_failure_message(args, &output));
}View on GitHub (pinned to a747e741ae)
Solutions
- Close competing git/jj processes (IDE background fetches, other terminals) and re-run the sync
- If no process holds the lock, remove the stale lock: `rm .git/index.lock`
- Retry the sync operation after clearing contention
- Increase/inspect JJ_GIT_EXPORT_LOCK_RETRY_DELAYS_MS if contention is frequent
Example fix
// before: stale lock present $ rm .git/index.lock // after: retry succeeds $ <sync command>
Defensive patterns
Strategy: retry
Validate before calling
use std::path::Path;
// before running sync, ensure no stale git index lock
let lock = Path::new(".git/index.lock");
if lock.exists() {
eprintln!(".git/index.lock exists; close other git/jj processes or remove it before syncing");
} Try / catch
// retry the sync after clearing the lock
for attempt in 0..3 {
match run_sync() {
Err(e) if e.to_string().contains("Git index stayed locked") => {
let _ = std::fs::remove_file(".git/index.lock");
std::thread::sleep(std::time::Duration::from_millis(500 * (attempt + 1)));
}
other => { other?; break; }
}
} Prevention
- Don't run git/jj commands in the same repo concurrently (IDEs, hooks, background fetch)
- Clean up .git/index.lock after crashed processes before syncing
- Increase JJ_GIT_LOCK retry delays if lock contention is common
- Close IDE auto-fetch while running long sync operations
When it happens
Trigger: `jj git export` was run, its output matched is_git_index_lock_error, and every retry attempt (JJ_GIT_EXPORT_LOCK_RETRY_DELAYS_MS.len() times) still hit the index lock.
Common situations: Another git/jj process (IDE auto-fetch, git status hook, background sync) holds .git/index.lock concurrently; a crashed git process left a stale lock file; parallel tooling operating on the same repo.
Related errors
- Lin.app is not running
- derived shared repo root {} does not contain .git
- Git index lock detected during merge. Remove stale .git/inde
- git push {} {} failed
- sync is blocked
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/63f90df9c1e2a24f.
Report an issue: GitHub.