SQLiteError: database is locked
What causes this
Bun has a built-in SQLite driver (bun:sqlite) that’s fast but comes with SQLite’s inherent limitations. The “database is locked” error means another connection or process is writing to the database and SQLite can’t handle concurrent writes.
Common causes:
- Multiple processes or workers writing to the same SQLite file simultaneously
- A long-running write transaction blocking other writes
- The database file is on a network filesystem (NFS, SMB) which doesn’t support SQLite’s locking
- A crashed process left a stale lock file (
database.db-wal,database.db-shm) - The database path doesn’t exist (for “not found” errors)
Fix 1: Enable WAL mode
Write-Ahead Logging dramatically improves concurrent access:
import { Database } from 'bun:sqlite';
const db = new Database('mydb.sqlite');
db.exec('PRAGMA journal_mode = WAL');
WAL mode allows multiple readers while one writer is active. It’s the single most impactful fix for SQLite concurrency.
Fix 2: Use a single database connection
Don’t create multiple Database instances for the same file:
// ❌ Multiple connections cause locking issues
function getUser(id: number) {
const db = new Database('mydb.sqlite');
return db.query('SELECT * FROM users WHERE id = ?').get(id);
}
// ✅ Reuse a single connection
const db = new Database('mydb.sqlite');
db.exec('PRAGMA journal_mode = WAL');
function getUser(id: number) {
return db.query('SELECT * FROM users WHERE id = ?').get(id);
}
Fix 3: Add busy timeout
Tell SQLite to wait instead of immediately failing:
const db = new Database('mydb.sqlite');
db.exec('PRAGMA busy_timeout = 5000'); // wait up to 5 seconds
Fix 4: Remove stale lock files
If a process crashed, it may have left lock files:
# Check for lock files
ls -la mydb.sqlite*
# If the database isn't in use, remove the WAL files
rm mydb.sqlite-wal mydb.sqlite-shm
Only do this when you’re sure no process is using the database.
Fix 5: Fix the database path
For “not found” errors:
import { Database } from 'bun:sqlite';
import { existsSync, mkdirSync } from 'fs';
import { dirname } from 'path';
const dbPath = './data/mydb.sqlite';
// Ensure the directory exists
mkdirSync(dirname(dbPath), { recursive: true });
// create: true creates the file if it doesn't exist
const db = new Database(dbPath, { create: true });
Fix 6: Don’t use SQLite on network filesystems
SQLite’s locking doesn’t work reliably on NFS or SMB:
// ❌ Network path
const db = new Database('/mnt/shared/mydb.sqlite');
// ✅ Local path
const db = new Database('./data/mydb.sqlite');
If you need shared access across machines, use PostgreSQL or MySQL instead.
How to prevent it
- Always enable WAL mode — there’s almost no reason not to
- Use a single database connection per process, exported from a shared module
- Set
PRAGMA busy_timeoutto handle brief lock contention gracefully - Keep SQLite databases on local filesystems, never on network mounts
- For high-concurrency apps, consider switching to PostgreSQL — SQLite is best for single-process or low-write workloads