fix: Enhance error classification for PRAGMA integrity check failures
Apply the same enhanced error classification to PRAGMA integrity_check failures as we do for database open failures. This ensures users see detailed, actionable recovery steps regardless of which stage the corruption is detected (open vs integrity check). Tested with real corruption scenarios - all error paths now provide specific recovery guidance with exact commands.
This commit is contained in:
@@ -317,28 +317,66 @@ func CheckDatabaseIntegrity(path string) DoctorCheck {
|
|||||||
// This checks the entire database for corruption
|
// This checks the entire database for corruption
|
||||||
rows, err := db.Query("PRAGMA integrity_check")
|
rows, err := db.Query("PRAGMA integrity_check")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Classify the error type for better recovery guidance
|
||||||
|
errMsg := err.Error()
|
||||||
|
var errorType string
|
||||||
|
var recoverySteps string
|
||||||
|
|
||||||
// Check if JSONL recovery is possible
|
// Check if JSONL recovery is possible
|
||||||
jsonlCount, _, jsonlErr := CountJSONLIssues(filepath.Join(beadsDir, "issues.jsonl"))
|
jsonlCount, _, jsonlErr := CountJSONLIssues(filepath.Join(beadsDir, "issues.jsonl"))
|
||||||
if jsonlErr != nil {
|
if jsonlErr != nil {
|
||||||
jsonlCount, _, jsonlErr = CountJSONLIssues(filepath.Join(beadsDir, "beads.jsonl"))
|
jsonlCount, _, jsonlErr = CountJSONLIssues(filepath.Join(beadsDir, "beads.jsonl"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Classify error type (same logic as opening errors)
|
||||||
|
if strings.Contains(errMsg, "database is locked") {
|
||||||
|
errorType = "Database is locked"
|
||||||
|
recoverySteps = "1. Check for running bd processes: ps aux | grep bd\n" +
|
||||||
|
"2. Kill any stale processes\n" +
|
||||||
|
"3. Remove stale locks: rm .beads/beads.db-shm .beads/beads.db-wal .beads/daemon.lock\n" +
|
||||||
|
"4. Retry: bd doctor --fix"
|
||||||
|
} else if strings.Contains(errMsg, "not a database") || strings.Contains(errMsg, "file is not a database") {
|
||||||
|
errorType = "File is not a valid SQLite database"
|
||||||
if jsonlErr == nil && jsonlCount > 0 {
|
if jsonlErr == nil && jsonlCount > 0 {
|
||||||
return DoctorCheck{
|
recoverySteps = fmt.Sprintf("Database file is corrupted beyond repair.\n\n"+
|
||||||
Name: "Database Integrity",
|
"Recovery steps:\n"+
|
||||||
Status: StatusError,
|
"1. Backup corrupt database: mv .beads/beads.db .beads/beads.db.broken\n"+
|
||||||
Message: fmt.Sprintf("Failed to run integrity check (JSONL has %d issues for recovery)", jsonlCount),
|
"2. Rebuild from JSONL (%d issues): bd doctor --fix --force --source=jsonl\n"+
|
||||||
Detail: err.Error(),
|
"3. Verify: bd stats", jsonlCount)
|
||||||
Fix: "Run 'bd doctor --fix' to recover from JSONL backup",
|
} else {
|
||||||
|
recoverySteps = "Database file is corrupted and no JSONL backup found.\n" +
|
||||||
|
"Manual recovery required:\n" +
|
||||||
|
"1. Restore from git: git checkout HEAD -- .beads/issues.jsonl\n" +
|
||||||
|
"2. Rebuild database: bd doctor --fix --force"
|
||||||
|
}
|
||||||
|
} else if strings.Contains(errMsg, "migration") || strings.Contains(errMsg, "validation failed") {
|
||||||
|
errorType = "Database migration or validation failed"
|
||||||
|
if jsonlErr == nil && jsonlCount > 0 {
|
||||||
|
recoverySteps = fmt.Sprintf("Database has validation errors (possibly orphaned dependencies).\n\n"+
|
||||||
|
"Recovery steps:\n"+
|
||||||
|
"1. Backup database: mv .beads/beads.db .beads/beads.db.broken\n"+
|
||||||
|
"2. Rebuild from JSONL (%d issues): bd doctor --fix --force --source=jsonl\n"+
|
||||||
|
"3. Verify: bd stats\n\n"+
|
||||||
|
"Alternative: bd doctor --fix --force (attempts to repair in-place)", jsonlCount)
|
||||||
|
} else {
|
||||||
|
recoverySteps = "Database validation failed and no JSONL backup available.\n" +
|
||||||
|
"Try: bd doctor --fix --force"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorType = "Failed to run integrity check"
|
||||||
|
if jsonlErr == nil && jsonlCount > 0 {
|
||||||
|
recoverySteps = fmt.Sprintf("Run 'bd doctor --fix --force --source=jsonl' to rebuild from JSONL (%d issues)", jsonlCount)
|
||||||
|
} else {
|
||||||
|
recoverySteps = "Run 'bd doctor --fix --force' to attempt recovery"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DoctorCheck{
|
return DoctorCheck{
|
||||||
Name: "Database Integrity",
|
Name: "Database Integrity",
|
||||||
Status: StatusError,
|
Status: StatusError,
|
||||||
Message: "Failed to run integrity check",
|
Message: errorType,
|
||||||
Detail: err.Error(),
|
Detail: fmt.Sprintf("%s\n\nError: %s", recoverySteps, errMsg),
|
||||||
Fix: "Run 'bd doctor --fix' to back up the corrupt DB and rebuild from JSONL (if available), or restore from backup",
|
Fix: "See recovery steps above",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user