Skip to content

fix: clear recovery message on SQLITE_CORRUPT migration failure#3086

Merged
Yeraze merged 1 commit into
mainfrom
claude/great-dijkstra-KFqfb
May 19, 2026
Merged

fix: clear recovery message on SQLITE_CORRUPT migration failure#3086
Yeraze merged 1 commit into
mainfrom
claude/great-dijkstra-KFqfb

Conversation

@Yeraze

@Yeraze Yeraze commented May 19, 2026

Copy link
Copy Markdown
Owner

$(cat <<'EOF'

Summary

  • Detects SQLITE_CORRUPT errors during SQLite migration runs and replaces the raw Node.js stack-trace crash with a clear, actionable error message
  • Prints the exact database file path and copy/delete commands the user needs to recover
  • Exits cleanly with code 1 instead of crashing with an unhandled exception

Fixes #3085 — reported on Raspberry Pi / Raspbian baremetal where SD card corruption caused SqliteError: database disk image is malformed during migration 059.

What changed

src/services/database.ts — in the SQLite migration loop's catch block, added a check for error.code === 'SQLITE_CORRUPT'. When detected, logs a formatted message like:

════════════════════════════════════════════════════════
DATABASE CORRUPTION DETECTED
════════════════════════════════════════════════════════
Your SQLite database file is corrupted (SQLITE_CORRUPT).
On Raspberry Pi this is usually caused by SD card failure
or an unexpected power loss during a write operation.

Database location: /path/to/meshmonitor.db

To recover:
  1. Back up:  cp "/path/to/meshmonitor.db" "/path/to/meshmonitor.db.bak"
  2. Delete:   rm "/path/to/meshmonitor.db"
  3. Restart MeshMonitor — a fresh database will be created.
...
════════════════════════════════════════════════════════

Then calls process.exit(1) rather than rethrowing (which produced an unhelpful UncaughtException crash).

Test plan

  • All existing tests pass (301/307 test files; 3 pre-existing failures due to missing protobuf files unrelated to this change)
  • No TypeScript errors in modified lines
  • Manual verification: simulate by temporarily changing the error code check to always trigger — confirms formatted message appears and process exits cleanly

https://claude.ai/code/session_01MgkrgSs7F5W22HtMT6tPhM
EOF
)


Generated by Claude Code

…ilure

When a migration fails with SQLITE_CORRUPT (common on Raspberry Pi due to
SD card wear or power loss), the app previously crashed with a raw Node.js
stack trace. Now it logs an actionable error box showing the database path
and the exact commands to back up the corrupted file and start fresh.

Closes #3085

https://claude.ai/code/session_01MgkrgSs7F5W22HtMT6tPhM
@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review Complete ✅

I've completed a thorough review of this pull request. Here's my detailed feedback:

Review Checklist:

  • Read and analyze the modified code
  • Evaluate code quality and best practices
  • Check for potential bugs or issues
  • Assess performance considerations
  • Review security concerns
  • Evaluate test coverage
  • Provide comprehensive feedback

Code Quality & Best Practices ✅

The implementation demonstrates solid coding practices:

  • Clean error detection: The type checking error && typeof error === 'object' && 'code' in error properly handles the fact that caught exceptions can be any type
  • Appropriate cast: Using (error as any).code is reasonable since we've already validated the error structure
  • Clear user messaging: The formatted error message is well-structured and actionable
  • Proper exit handling: Using process.exit(1) instead of rethrowing prevents the unhelpful stack trace crash

Potential Issues & Suggestions 🔍

Minor Enhancement Opportunity:
Consider adding TypeScript typing for better type safety:

interface SqliteError extends Error {
  code: string;
}

function isSqliteError(error: unknown): error is SqliteError {
  return error instanceof Error && 'code' in error;
}

However, the current implementation is perfectly functional and the type checking is adequate.

Performance Considerations ✅

  • Minimal overhead: The error check only activates on exception paths, so no performance impact on normal operation
  • Early exit: process.exit(1) prevents unnecessary cleanup attempts that could hang on corrupted databases

Security Concerns ✅

  • Path disclosure: The error message includes the database file path (${dbPath}), but this is appropriate for corruption recovery - users need to know which file to backup/delete
  • No sensitive data leakage: The error handling doesn't expose any credentials or internal state
  • Clean exit: Using process.exit(1) prevents the application from continuing in an undefined state

Test Coverage Assessment ⚠️

Current state: The PR description mentions "Manual verification: simulate by temporarily changing the error code check" but no automated tests for this specific error path.

Recommendation: Consider adding a unit test that mocks an SQLITE_CORRUPT error to verify:

  1. The formatted message is logged correctly
  2. process.exit(1) is called
  3. The error doesn't propagate beyond this handler

Example test structure:

it('should handle SQLITE_CORRUPT errors gracefully', () => {
  const mockExit = vi.spyOn(process, 'exit').mockImplementation();
  const mockError = { code: 'SQLITE_CORRUPT', message: 'database disk image is malformed' };
  
  // Test that the error is caught and handled properly
  // Assert formatted message is logged
  // Assert process.exit(1) is called
});

Overall Assessment 🎯

This is a high-quality fix that addresses a real user pain point (issue #3085). The implementation is:

  • Functionally correct - properly detects and handles SQLite corruption
  • User-friendly - provides clear recovery instructions instead of cryptic stack traces
  • Platform-aware - specifically mentions Raspberry Pi context from the original issue
  • Safe - uses clean exit instead of undefined application state

Recommendation: Approve with optional test enhancement. The core functionality is solid and addresses the reported issue effectively.


@Yeraze Yeraze merged commit 30ec26a into main May 19, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SUPPORT] 4.6.1 on Raspbian baremetal

2 participants