{"record":{"id":"2678a2279c1fdbf7","repo":"gitbutlerapp/gitbutler","slug":"database-file-at-db-path-has-max-attempts-cor","errorCode":null,"errorMessage":"Database file at '{db_path} has {max_attempts} corrupted copies - giving up, application probably won't work","messagePattern":"Database file at '(.+?) has (.+?) corrupted copies - giving up, application probably won't work","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/gitbutler-tauri/src/projects.rs","lineNumber":156,"sourceCode":"            if backup_path.is_file() {\n                continue;\n            }\n\n            if let Err(err) = std::fs::rename(&db_path, &backup_path) {\n                bail!(\n                    \"Failed to rename {} to {} - application may fail to startup: {err}\",\n                    db_path.display(),\n                    backup_path.display()\n                );\n            }\n\n            return Ok(Some(format!(\n                \"Could not open db file at '{}'.\\nIt was moved to {} for recovery. \\n\\nError was: {err}\",\n                db_path.display(),\n                backup_path.display()\n            )));\n        }\n        bail!(\n            \"Database file at '{db_path} has {max_attempts} corrupted copies - giving up, application probably won't work\",\n            db_path = db_path.display()\n        );\n    }\n    Ok(None)\n}\n\n/// Return an error message that\nfn warn_about_filters_and_git_lfs(repo: &gix::Repository) -> anyhow::Result<Option<String>> {\n    let index = repo.index_or_empty()?;\n    let mut cache = repo.attributes_only(\n        &index,\n        gix::worktree::stack::state::attributes::Source::WorktreeThenIdMapping,\n    )?;\n    let mut attrs = cache.selected_attribute_matches(Some(\"filter\"));\n    let mut all_filters = BTreeSet::<String>::new();\n    let mut files_with_filter = Vec::new();\n    for entry in index.entries() {","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-tauri/src/projects.rs#L138-L174","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: 254 quarantine copies, startup gives up\nls \"$DATA_DIR\"/*.maybe-broken-* | wc -l\n# after: archive them, keep the newest for inspection, relaunch\nmkdir -p ~/gb-db-backups\nmv \"$DATA_DIR\"/*.maybe-broken-* ~/gb-db-backups/\nsqlite3 ~/gb-db-backups/$(ls -1 ~/gb-db-backups | tail -1) 'PRAGMA integrity_check;'","handlingStrategy":"validation","validationCode":"let quarantined: Vec<_> = std::fs::read_dir(data_dir)?\n    .filter_map(|e| e.ok())\n    .filter(|e| e.file_name().to_string_lossy().contains(\".maybe-broken-\"))\n    .collect();\nif quarantined.len() > 50 {\n    // archive them now, before startup recovery exhausts its 255 slots\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["database","sqlite","corruption","startup","recovery"],"backgroundTag":"database-corruption-recovery-exhausted","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}