gitbutlerapp/gitbutler · warning · anyhow::Error
Project at '{}' is already being refreshed in the background
Error message
Project at '{}' is already being refreshed in the background by another GitButler instance What it means
Same `try_exclusive_inter_process_access()` path as the write lock, but with `LockScope::BackgroundRefreshOperations`, which locks `background-refresh.lock` instead of `project.lock`. It means another GitButler instance is currently running the background refresh for this project. User-driven operations may still proceed; only the background refresh refuses to overlap.
Source
Thrown at crates/but-core/src/sync.rs:68
let got_lock = lock
.try_lock()
.context("Failed to check if lock is taken")?;
if !got_lock {
let error_message = match scope {
LockScope::AllOperations => {
format!(
"Project at '{}' is already opened for writing by another GitButler instance",
project_data.display()
)
}
LockScope::BackgroundRefreshOperations => {
format!(
"Project at '{}' is already being refreshed in the background by another GitButler instance",
project_data.display()
)
}
};
bail!(error_message);
}
Ok(lock)
}
/// Return a guard for exclusive (read+write) *in-process* repository access for the project at
/// `git_dir`, blocking while waiting for someone else in this process to release it, or for all
/// readers to disappear. Locking is fair.
/// Also use `project_data_dir` if `Some` to create an *inter-process* exclusive lock.
/// Creating, opening, or locking that file is best-effort. Failures are logged and ignored, so
/// the hard guarantee provided by this function remains in-process exclusivity only.
///
/// If the current process inherits Git's commit-hook environment (`GIT_EDITOR=:` together with
/// `GIT_INDEX_FILE`), acquiring the inter-process lock becomes non-blocking: if another process
/// already holds it, we continue without that file lock instead of waiting. This avoids
/// deadlocking hook re-entry when the parent GitButler command is already holding the same
/// inter-process lock while waiting for the hook to finish.
/// If `project_data_dir` is `None`, no inter-process lock is obtained.
///View on GitHub (pinned to caf1f223d3)
Solutions
- Treat as transient: the refresh lock is short-lived, so simply retry after the other instance's refresh completes.
- Ensure only one long-running GitButler instance per project data directory; exit extras.
- If it persists, identify the holder via `lsof <project_data>/background-refresh.lock` and let it finish or stop it.
- Schedule your refresh-triggering work to avoid racing the desktop app's refresh window.
Defensive patterns
Strategy: retry
Try / catch
// Background-refresh contention is transient: back off and retry
let mut delay = std::time::Duration::from_millis(500);
loop {
match try_exclusive_inter_process_access(&dir, LockScope::BackgroundRefreshOperations) {
Ok(lock) => break lock,
Err(err) if err.to_string().contains("being refreshed in the background") => {
std::thread::sleep(delay);
delay = (delay * 2).min(std::time::Duration::from_secs(30));
}
Err(err) => return Err(err),
}
} Prevention
- Jitter background-refresh schedules so multiple instances don't collide on the same cadence.
- Keep only one long-lived GitButler process per project; secondary CLIs should skip refresh if the lock is taken.
- Log at info (not error) when this lock is contended — it's expected coexistence behavior.
When it happens
Trigger: Two instances both scheduling background refreshes for the same project data directory — desktop app's periodic refresh while a second app instance or a long-lived daemon also refreshes; rapid restarts where the old process's refresh task has not finished.
Common situations: App restarted quickly while the previous process was mid-refresh; a GitButler service/agent and the interactive app pointed at one project.
Related errors
- Project at '{}' is already opened for writing by another Git
- diffing-thread crashed
- Failed to communicate with LM Studio server: ${error instanc
- Invalid response: ${JSON.stringify(result)}
- When using GitButler's API to summarize code, you must be lo
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/2411cc81db9d0a71.
Report an issue: GitHub.