gitbutlerapp/gitbutler · critical

There were already {max_attempts} backup project files - giv

Error message

There were already {max_attempts} backup project files - giving up

What it means

Thrown by Controller::assure_app_can_startup_or_fix_it when the projects.json file fails to load and the self-healing quarantine loop finds no free backup slot. On each corrupt load the controller renames the broken file to projects.json.maybe-broken-NN (NN in 1..255) and tells the user to reopen the app to start fresh. If every one of those backup names already exists, it gives up without touching the original file, so 254+ prior recoveries accumulated without cleanup and a persistent corruption source is likely.

Source

Thrown at crates/gitbutler-project/src/controller.rs:156

                    if backup_path.is_file() {
                        continue;
                    }

                    if let Err(err) = std::fs::rename(&projects_path, &backup_path) {
                        tracing::error!(
                            "Failed to rename {} to {} - application may fail to startup: {err}",
                            projects_path.display(),
                            backup_path.display()
                        );
                    }

                    bail!(
                        "Could not open projects file at '{}'.\nIt was moved to {}.\nReopen or refresh the app to start fresh.\nError was: {probably_file_load_err}",
                        projects_path.display(),
                        backup_path.display()
                    );
                }
                bail!("There were already {max_attempts} backup project files - giving up")
            }
        }
    }
}

impl Controller {
    pub(crate) fn from_path(path: impl Into<PathBuf>) -> Self {
        let path = path.into();
        Self {
            projects_storage: storage::Storage::from_path(&path),
            local_data_dir: path,
        }
    }

    pub(crate) fn add_with_best_effort<P: AsRef<Path>>(
        &self,
        worktree_dir: P,
    ) -> Result<AddProjectOutcome> {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Close the app and archive or delete the accumulated projects.json.maybe-broken-* files in the app data directory, then reopen - the quarantine loop gets free slots and the app starts fresh
  2. Inspect the newest .maybe-broken-* copy to recover project worktree paths and re-add the projects manually
  3. Eliminate the root cause of repeated corruption: run only one app instance per data dir, stop cloud-syncing the data dir, and check disk health
  4. If projects.json was hand-edited or truncated, fix the JSON in place instead of relying on the quarantine loop

Example fix

# before: recovery exhausted, startup gives up
ls "$DATA_DIR"/projects.json.maybe-broken-* | wc -l   # 254 files
# after: archive old quarantines so slots free up, then relaunch
mkdir -p ~/gb-project-backups
mv "$DATA_DIR"/projects.json.maybe-broken-* ~/gb-project-backups/
Defensive patterns

Strategy: validation

Validate before calling

// before Controller startup, ensure quarantine slots are free
let stale: Vec<_> = std::fs::read_dir(&data_dir)?
    .filter_map(|e| e.ok())
    .filter(|e| e.file_name().to_string_lossy().starts_with("projects.json.maybe-broken-"))
    .collect();
if !stale.is_empty() {
    // archive them out of the data dir before loading projects
}

Prevention

When it happens

Trigger: Loading the project list from local_data_dir/projects.json returns an error (malformed JSON, partial write, disk fault), AND files named projects.json.maybe-broken-01 through -254 already exist in the same directory, so the 'for round in 1..max_attempts' loop never finds an unused name and falls through to the bail.

Common situations: Two app instances (or app + CLI) sharing one data directory and corrupting each other's writes; force-quit or crashing app leaving truncated JSON on every start; disk faults or cloud-sync tools (Dropbox/OneDrive) damaging the file; repeated recovery with nobody deleting the .maybe-broken-* leftovers; running an older app version against a newer file format.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/4b76a6a74a64cfec. Report an issue: GitHub.