From 749204f96bb1729916f345d56456646310f1302a Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Thu, 6 Nov 2025 21:38:51 +0800 Subject: [PATCH] fix: database connection error causing navigation redirect on iOS/Android Handle "Connection already exists" error when initializing SQLite database on Capacitor platforms. The native connection can persist across app restarts while the JavaScript connection Map is empty, causing a mismatch. When createConnection fails with "already exists": - Check if connection exists in JavaScript Map and retrieve it if present - If not in Map, close the native connection and recreate to sync both sides - Handle "already open" errors gracefully when opening existing connections This fixes the issue where clicking "Backup Identifier Seed" would redirect to StartView instead of SeedBackupView due to database initialization failures in the router navigation guard. Fixes navigation issue on both iOS and Android platforms. --- .../platforms/CapacitorPlatformService.ts | 94 +++++++++++++++++-- 1 file changed, 85 insertions(+), 9 deletions(-) diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index 51fb9ce5..fe804f8e 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -91,16 +91,92 @@ export class CapacitorPlatformService } try { - // Create/Open database - this.db = await this.sqlite.createConnection( - this.dbName, - false, - "no-encryption", - 1, - false, - ); + // Try to create/Open database connection + try { + this.db = await this.sqlite.createConnection( + this.dbName, + false, + "no-encryption", + 1, + false, + ); + } catch (createError: unknown) { + // If connection already exists, try to retrieve it or handle gracefully + const errorMessage = + createError instanceof Error + ? createError.message + : String(createError); + const errorObj = + typeof createError === "object" && createError !== null + ? (createError as { errorMessage?: string; message?: string }) + : {}; + + const fullErrorMessage = + errorObj.errorMessage || errorObj.message || errorMessage; + + if (fullErrorMessage.includes("already exists")) { + logger.debug( + "[CapacitorPlatformService] Connection already exists on native side, attempting to retrieve", + ); + // Check if connection exists in JavaScript Map + const isConnResult = await this.sqlite.isConnection( + this.dbName, + false, + ); + if (isConnResult.result) { + // Connection exists in Map, retrieve it + this.db = await this.sqlite.retrieveConnection(this.dbName, false); + logger.debug( + "[CapacitorPlatformService] Successfully retrieved existing connection from Map", + ); + } else { + // Connection exists on native side but not in JavaScript Map + // This can happen when the app is restarted but native connections persist + // Try to close the native connection first, then create a new one + logger.debug( + "[CapacitorPlatformService] Connection exists natively but not in Map, closing and recreating", + ); + try { + await this.sqlite.closeConnection(this.dbName, false); + } catch (closeError) { + // Ignore close errors - connection might not be properly tracked + logger.debug( + "[CapacitorPlatformService] Error closing connection (may be expected):", + closeError, + ); + } + // Now try to create the connection again + this.db = await this.sqlite.createConnection( + this.dbName, + false, + "no-encryption", + 1, + false, + ); + logger.debug( + "[CapacitorPlatformService] Successfully created connection after cleanup", + ); + } + } else { + // Re-throw if it's a different error + throw createError; + } + } - await this.db.open(); + // Open the connection if it's not already open + try { + await this.db.open(); + } catch (openError: unknown) { + const openErrorMessage = + openError instanceof Error ? openError.message : String(openError); + // If already open, that's fine - continue + if (!openErrorMessage.includes("already open")) { + throw openError; + } + logger.debug( + "[CapacitorPlatformService] Database connection already open", + ); + } // Set journal mode to WAL for better performance // await this.db.execute("PRAGMA journal_mode=WAL;");