gitbutlerapp/gitbutler · critical
Database file at '{db_path} has {max_attempts} corrupted cop
Error message
Database file at '{db_path} has {max_attempts} corrupted copies - giving up, application probably won't work What it means
Thrown when the startup db-quarantine loop finds every numbered backup name taken. Each failed open renames the db to <db_name>.maybe-broken-NN for NN in 1..255; if all slots already exist, the app gives up because the database keeps getting corrupted and no more quarantine copies fit. Like its projects.json twin (controller.rs), it signals repeated corruption with no cleanup.
Source
Thrown at crates/gitbutler-tauri/src/projects.rs:156
if backup_path.is_file() {
continue;
}
if let Err(err) = std::fs::rename(&db_path, &backup_path) {
bail!(
"Failed to rename {} to {} - application may fail to startup: {err}",
db_path.display(),
backup_path.display()
);
}
return Ok(Some(format!(
"Could not open db file at '{}'.\nIt was moved to {} for recovery. \n\nError was: {err}",
db_path.display(),
backup_path.display()
)));
}
bail!(
"Database file at '{db_path} has {max_attempts} corrupted copies - giving up, application probably won't work",
db_path = db_path.display()
);
}
Ok(None)
}
/// Return an error message that
fn warn_about_filters_and_git_lfs(repo: &gix::Repository) -> anyhow::Result<Option<String>> {
let index = repo.index_or_empty()?;
let mut cache = repo.attributes_only(
&index,
gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping,
)?;
let mut attrs = cache.selected_attribute_matches(Some("filter"));
let mut all_filters = BTreeSet::<String>::new();
let mut files_with_filter = Vec::new();
for entry in index.entries() {View on GitHub (pinned to caf1f223d3)
Solutions
- Close the app and delete or archive the accumulated <db_name>.maybe-broken-* files, then relaunch so quarantine can run again
- Check the newest quarantine copy with sqlite3 <file> 'PRAGMA integrity_check;' to confirm real corruption versus an environment problem
- Fix the root cause: one app instance, no cloud-sync on the data dir, current app version, healthy disk
- If the data matters, recover what you need from the newest good copy before letting the app start fresh
Example fix
# before: 254 quarantine copies, startup gives up ls "$DATA_DIR"/*.maybe-broken-* | wc -l # after: archive them, keep the newest for inspection, relaunch mkdir -p ~/gb-db-backups mv "$DATA_DIR"/*.maybe-broken-* ~/gb-db-backups/ sqlite3 ~/gb-db-backups/$(ls -1 ~/gb-db-backups | tail -1) 'PRAGMA integrity_check;'
Defensive patterns
Strategy: validation
Validate before calling
let quarantined: Vec<_> = std::fs::read_dir(data_dir)?
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".maybe-broken-"))
.collect();
if quarantined.len() > 50 {
// archive them now, before startup recovery exhausts its 255 slots
} Prevention
- Monitor and clean .maybe-broken-* files in the data dir
- Never run two app versions against one data dir
- Exclude the data dir from sync tools
- Investigate the first corruption instead of relaunching repeatedly
When it happens
Trigger: The project db open fails AND files <db_name>.maybe-broken-01 through -254 already exist in the data dir, so the 'for round in 1..max_attempts' loop never finds a free name and falls through to the bail.
Common situations: A persistent corruption source (failing disk, sync tool, two app versions fighting over the schema) corrupting the db on every launch; users relaunching repeatedly with each launch adding one quarantine copy; data dirs migrated wholesale including all old quarantine files.
Related errors
- Failed to rename {} to {} - application may fail to startup:
- There were already {max_attempts} backup project files - giv
- broker already configured
- FATAL: Couldn't open in-memory URL: {path_err}
- transcript contains malformed JSON before the final record
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/2678a2279c1fdbf7.
Report an issue: GitHub.