{"record":{"id":"ec29a18672d82982","repo":"laurent22/joplin","slug":"cannot-open-database-error-message-error","errorCode":null,"errorMessage":"Cannot open database: ${error.message ?? error}: ${JSON.stringify(options)}","messagePattern":"Cannot open database: (.+?): (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/lib/database.ts","lineNumber":64,"sourceCode":"\n\tpublic setLogger(l: Logger) {\n\t\tthis.logger_ = l;\n\t}\n\n\tpublic logger() {\n\t\treturn this.logger_;\n\t}\n\n\tpublic driver() {\n\t\treturn this.driver_;\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Open options vary per driver: better-sqlite3 expects { name }, web/RN drivers accept additional fields\n\tpublic async open(options: any) {\n\t\ttry {\n\t\t\tawait this.driver().open(options);\n\t\t} catch (error) {\n\t\t\tthrow new Error(`Cannot open database: ${error.message ?? error}: ${JSON.stringify(options)}`);\n\t\t}\n\n\t\tthis.logger().info('Database was open successfully');\n\t}\n\n\tpublic async close() {\n\t\ttry {\n\t\t\tawait this.driver().close?.();\n\t\t} catch (error) {\n\t\t\tthis.logger().warn('Failed to close database', error);\n\t\t}\n\t}\n\n\tpublic escapeField(field: string) {\n\t\tif (field === '*') return '*';\n\t\tconst p = field.split('.');\n\t\tif (p.length === 1) return `\\`${field}\\``;\n\t\tif (p.length === 2) return `${p[0]}.\\`${p[1]}\\``;","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/lib/database.ts#L46-L82","documentation":"Thrown by Database.open() when the underlying driver's open(options) rejects. The driver is platform-specific (better-sqlite3 on desktop, a web SQL driver, or an RN driver), and the options object — typically { name: <db file path> } — is JSON-serialised into the message. Common root causes are a locked/corrupt database file, missing file system permissions, or an invalid/missing database name.","triggerScenarios":"Calling database.open({ name: '/path/to/database.sqlite' }) when the driver cannot open the file: another process holds a lock on the SQLite file, the path is not writable, the file is corrupt (SQLITE_CORRUPT), the profile directory doesn't exist, or better-sqlite3 was not built for the current Node ABI.","commonSituations":"Two Joplin instances opening the same profile; the profile directory was deleted or is on a read-only mount; a crash left a stale -wal/-shm lock; a Node/Electron upgrade broke the native better-sqlite3 binding (ABI mismatch); antivirus locking the DB file on Windows.","solutions":["Ensure no other Joplin process has the same profile/database open.","Verify write permissions and existence of the profile directory and the database file path.","If corrupt, restore from backup or delete the database file so Joplin recreates it (data loss for that profile).","After a Node/Electron upgrade, rebuild native modules (yarn rebuild better-sqlite3) to fix ABI mismatches.","On Windows, exclude the profile folder from antivirus real-time scanning."],"exampleFix":"// before\nawait db.open({ name: dbPath }); // throws if locked/corrupt\n\n// after — surface the driver error and guard against concurrent opens\ntry {\n  await db.open({ name: dbPath });\n} catch (error) {\n  logger.error('Database open failed', error);\n  // recover: restore backup, recreate DB, or prompt user\n}","handlingStrategy":"try-catch","validationCode":"// Before opening, confirm the path is writable and not locked by another process\nimport shim from './shim';\nconst exists = await shim.fsDriver().exists(dbPath);\nconst parentDir = shim.fsDriver().dirname(dbPath);\nconst parentExists = await shim.fsDriver().exists(parentDir);\nif (!parentExists) {\n  // create the profile directory or surface the misconfiguration\n  return;\n}","typeGuard":"function isDatabaseOpenError(e: unknown): e is Error {\n  return e instanceof Error && /^Cannot open database/.test(e.message);\n}","tryCatchPattern":"try {\n  await database.open({ name: dbPath });\n} catch (error) {\n  if (isDatabaseOpenError(error)) {\n    // inspect the wrapped driver message; recover (backup/restore) or fail fast\n    logger.error('DB open failed', error);\n    throw error;\n  }\n  throw error;\n}","preventionTips":["Ensure only one process opens a given SQLite profile at a time.","Verify profile directory write permissions and existence before startup.","Rebuild native modules (better-sqlite3) after Node/Electron upgrades.","Back up the profile regularly so corrupt databases can be restored."],"tags":["database","sqlite","startup","native-modules","filesystem"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}