laurent22/joplin · critical · Error
Cannot open database: ${error.message ?? error}: ${JSON.stri
Error message
Cannot open database: ${error.message ?? error}: ${JSON.stringify(options)} What it means
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.
Source
Thrown at packages/lib/database.ts:64
public setLogger(l: Logger) {
this.logger_ = l;
}
public logger() {
return this.logger_;
}
public driver() {
return this.driver_;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Open options vary per driver: better-sqlite3 expects { name }, web/RN drivers accept additional fields
public async open(options: any) {
try {
await this.driver().open(options);
} catch (error) {
throw new Error(`Cannot open database: ${error.message ?? error}: ${JSON.stringify(options)}`);
}
this.logger().info('Database was open successfully');
}
public async close() {
try {
await this.driver().close?.();
} catch (error) {
this.logger().warn('Failed to close database', error);
}
}
public escapeField(field: string) {
if (field === '*') return '*';
const p = field.split('.');
if (p.length === 1) return `\`${field}\``;
if (p.length === 2) return `${p[0]}.\`${p[1]}\``;View on GitHub (pinned to 2654b33620)
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.
Example fix
// before
await db.open({ name: dbPath }); // throws if locked/corrupt
// after — surface the driver error and guard against concurrent opens
try {
await db.open({ name: dbPath });
} catch (error) {
logger.error('Database open failed', error);
// recover: restore backup, recreate DB, or prompt user
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before opening, confirm the path is writable and not locked by another process
import shim from './shim';
const exists = await shim.fsDriver().exists(dbPath);
const parentDir = shim.fsDriver().dirname(dbPath);
const parentExists = await shim.fsDriver().exists(parentDir);
if (!parentExists) {
// create the profile directory or surface the misconfiguration
return;
} Type guard
function isDatabaseOpenError(e: unknown): e is Error {
return e instanceof Error && /^Cannot open database/.test(e.message);
} Try / catch
try {
await database.open({ name: dbPath });
} catch (error) {
if (isDatabaseOpenError(error)) {
// inspect the wrapped driver message; recover (backup/restore) or fail fast
logger.error('DB open failed', error);
throw error;
}
throw error;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unknown profile version. Most likely this is an old version
- No item with ID ${itemId}
- Please specify the sync target path.
- Format "${format}" can only be exported to a file
- Only one output directory can be selected
AI-assisted analysis of laurent22/joplin@2654b33620 (2026-08-12).
Data as JSON: /api/errors/ec29a18672d82982.
Report an issue: GitHub.