How to fix SQLITE_LOCKED_SHAREDCACHE: Conflict within shared cache in SQLite

SQLiteINTERMEDIATEHIGHUpdated Dec 29, 2025· 169 views

When using SQLite's shared-cache mode, connections within the same process experience table-level locking conflicts that return SQLITE_LOCKED_SHAREDCACHE (error code 6). Unlike SQLITE_BUSY (which occurs between separate processes), this error indicates a locking conflict between connections sharing the same cache and cannot be resolved with timeout settings.

Quick fix

Fixes SQLITE_LOCKED (6)

# Python - sqlite3
import sqlite3
conn = sqlite3.connect('database.db')  # Shared cache disabled by default

# If it was enabled, disable it
sqlite3.enable_shared_cache(False)
  1. Disable shared-cache mode (recommended solution)
  2. Switch to WAL mode for better concurrency
  3. Use read_uncommitted pragma as a temporary workaround
  4. Implement connection queuing and serialization
sqlite — error
# Python - sqlite3
import sqlite3
conn = sqlite3.connect('database.db') # Shared cache disabled by default
# If it was enabled, disable it
sqlite3.enable_shared_cache(False)
SQLITE_LOCKED_SHAREDCACHE: Conflict within shared cache

What this error means

The SQLITE_LOCKED_SHAREDCACHE error is specific to SQLite's shared-cache mode, an optional feature that allows multiple connections within the same process to share a memory cache. In shared-cache mode, SQLite enforces table-level locking instead of database-level locking. When two or more connections attempt conflicting operations on the same table (or when a schema modification is pending), one connection receives SQLITE_LOCKED_SHAREDCACHE. This error differs fundamentally from SQLITE_BUSY. The SQLITE_BUSY error (code 5) occurs when separate database processes conflict over the database file. In contrast, SQLITE_LOCKED (code 6) occurs between connections in the same process that share a cache. The error cannot be resolved with busy timeouts or retry loops—if a lock conflict occurs in shared-cache mode, the operation has immediately failed. Shared-cache mode was designed in 2006 to save memory on embedded platforms but has been largely superseded by WAL (Write-Ahead Logging) mode, which provides better concurrency without the same locking limitations.

Typical symptoms

  • Application receives SQLITE_LOCKED_SHAREDCACHE or error code 6 when executing queries
  • Error occurs intermittently in multi-threaded or asynchronous code
  • Multiple connections to the same in-memory (:memory:) database with shared cache enabled fail
  • Schema modification operations (CREATE TABLE, DROP TABLE, ALTER TABLE) block concurrent reads
  • Transactions with pending schema changes cause readers on the same shared cache to receive locking errors
  • Retries and exponential backoff do not resolve the error

Common causes

  1. Shared-cache mode is enabled when multiple connections access the same database in the same process
  2. Two or more connections attempt concurrent write operations to the same table
  3. A connection holds a write transaction on a table while another connection tries to read or write to it
  4. Schema-level locking occurs when one connection modifies the database schema while others try to access tables
  5. One connection is holding an open transaction with uncommitted schema changes, blocking readers with SQLITE_LOCKED_SHAREDCACHE even if they use PRAGMA read_uncommitted
  6. Reader connections attempt to access a table while a writer is holding a schema-level lock

How to fix "SQLITE_LOCKED_SHAREDCACHE: Conflict within shared cache"

1Disable shared-cache mode (recommended solution)

Shared-cache mode is obsolete and causes more problems than it solves. Disable it in your connection string or initialization code:

python
# Python - sqlite3
import sqlite3
conn = sqlite3.connect('database.db')  # Shared cache disabled by default

# If it was enabled, disable it
sqlite3.enable_shared_cache(False)
javascript
// Node.js - better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('database.db');
// better-sqlite3 does not use shared cache mode by default

// JavaScript - sqlite3
const sqlite3 = require('sqlite3');
const db = new sqlite3.Database('database.db');
// sqlite3 does not use shared cache mode by default
go
// Go - go-sqlite3
import _ "github.com/mattn/go-sqlite3"
// Shared cache can be enabled via URI parameter
// To disable: do NOT use ?cache=shared in the connection string
db, err := sql.Open("sqlite3", "file:database.db")
2Switch to WAL mode for better concurrency

WAL (Write-Ahead Logging) mode provides superior concurrent access without the locking problems of shared-cache mode. Enable WAL mode to allow readers and writers to operate simultaneously:

python
import sqlite3
conn = sqlite3.connect('database.db')
conn.execute('PRAGMA journal_mode=WAL')
conn.close()
javascript
// Node.js - better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('database.db');
db.pragma('journal_mode = WAL');
sql
-- In SQLite directly
PRAGMA journal_mode=WAL;

WAL mode allows readers to continue accessing the database while writers commit changes, eliminating the need for shared-cache mode's table-level locks.

3Use read_uncommitted pragma as a temporary workaround

If you cannot immediately disable shared-cache mode, enable read_uncommitted to allow readers to bypass read locks. Note this only helps readers, not writers:

python
import sqlite3
conn = sqlite3.connect('database.db')
conn.execute('PRAGMA read_uncommitted=1')
# Now SELECT queries will not attempt to obtain read locks
javascript
// Node.js - better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('database.db');
db.pragma('read_uncommitted = 1');
sql
PRAGMA read_uncommitted=1;

This changes the isolation level from serialized (default) to read-uncommitted, allowing readers to access tables even when writers hold table locks. However, this is not a complete solution for write-write conflicts.

4Implement connection queuing and serialization

As a temporary measure, reduce concurrency by serializing database access through a single connection or queue:

python
import sqlite3
from threading import Lock

class DatabaseQueue:
    def __init__(self, db_path):
        self.conn = sqlite3.connect(db_path)
        self.lock = Lock()

    def execute(self, query, params=()):
        with self.lock:
            return self.conn.execute(query, params).fetchall()

db = DatabaseQueue('database.db')
# All operations are serialized through the lock
result = db.execute('SELECT * FROM users WHERE id = ?', (1,))
javascript
// Node.js - using a mutex or queue
const Database = require('better-sqlite3');
const Mutex = require('async-lock');

class DatabaseQueue {
    constructor(dbPath) {
        this.db = new Database(dbPath);
        this.lock = new Mutex();
    }

    async execute(query, params = []) {
        return this.lock.acquire('db', () => {
            return this.db.prepare(query).all(...params);
        });
    }
}

const db = new DatabaseQueue('database.db');
// All operations serialize through the lock
5Commit transactions promptly

Ensure all transactions (especially those modifying the schema) are committed or rolled back immediately. Long-running or uncommitted transactions block other connections:

python
import sqlite3
conn = sqlite3.connect('database.db')

# GOOD: Transaction is short and committed immediately
try:
    conn.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
    conn.commit()
except Exception as e:
    conn.rollback()
    print(f"Error: {e}")

# BAD: Transaction left open indefinitely
cursor = conn.cursor()
cursor.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
# If this transaction is not committed, other connections will be blocked
javascript
// Node.js - better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('database.db');

// GOOD: Use transactions and commit immediately
const transaction = db.transaction(() => {
    db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
});
transaction();

// BAD: Manual transaction left open
db.exec('BEGIN');
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
// If COMMIT is not called, schema changes block all other connections
6Review driver/library documentation for shared-cache

Verify that shared-cache mode is not accidentally enabled in your application:

- Python sqlite3: Shared cache is disabled by default. Only enabled via sqlite3.enable_shared_cache(True)
- Node.js better-sqlite3: Does not support shared-cache mode
- Node.js sqlite3: Does not enable shared-cache by default
- Go go-sqlite3: Only enabled if ?cache=shared is in the connection URI
- Rust rusqlite: Shared cache disabled by default
- Java JDBC: Not applicable; each JVM process gets its own SQLite instance

If you see shared-cache enabled in your configuration, remove it immediately.

Advanced notes

Schema-Level Locking Details: In shared-cache mode, any connection modifying the schema (CREATE TABLE, DROP TABLE, ALTER TABLE, etc.) holds a write-lock on sqlite_schema. All other connections attempting to read or modify any table will receive SQLITE_LOCKED_SHAREDCACHE until that schema transaction commits. Even connections using PRAGMA read_uncommitted=1 cannot bypass this.

Shared-Cache History: Shared-cache mode was introduced in SQLite 3.3.0 to save memory on embedded systems (specifically Symbian phones). However, WAL mode (introduced in SQLite 3.7.0, circa 2010) provides a superior solution for the same problem—allowing concurrent reads and writes without the sharp locking semantics of shared-cache.

Why Timeout Does Not Work: SQLITE_BUSY can be managed with sqlite3_busy_timeout() because it represents a transient file lock that will eventually be released. SQLITE_LOCKED_SHAREDCACHE, however, represents an immediate application-level conflict that will never resolve through waiting—the lock is permanent until one of the conflicting operations completes.

Unlock Notify API: Some SQLite bindings (like go-sqlite3) mention the unlock_notify API as a solution, but this is rarely implemented and requires careful multi-threaded code. Most applications benefit more from simply disabling shared-cache or using WAL mode.

Migration Path: If your application currently relies on shared-cache for memory efficiency or concurrency, migrate to:
1. WAL mode for improved concurrency (primary recommendation)
2. A client-server database (PostgreSQL, MySQL) if you need true multi-process concurrent access

Frequently asked questions

What does "SQLITE_LOCKED (6)" mean?
The SQLITE_LOCKED_SHAREDCACHE error is specific to SQLite's shared-cache mode, an optional feature that allows multiple connections within the same process to share a memory cache. In shared-cache mode, SQLite enforces table-level locking instead of database-level locking. When two or more connections attempt conflicting operations on the same table (or when a schema modification is pending), one connection receives SQLITE_LOCKED_SHAREDCACHE.
How do I fix "SQLITE_LOCKED (6)"?
When using SQLite's shared-cache mode, connections within the same process experience table-level locking conflicts that return SQLITE_LOCKED_SHAREDCACHE (error code 6). Unlike SQLITE_BUSY (which occurs between separate processes), this error indicates a locking conflict between connections sharing the same cache and cannot be resolved with timeout settings.
What causes "SQLITE_LOCKED (6)"?
Shared-cache mode is enabled when multiple connections access the same database in the same process Two or more connections attempt concurrent write operations to the same table A connection holds a write transaction on a table while another connection tries to read or write to it
Is the "SQLITE_LOCKED (6)" error serious?
Yes. This is a high-severity error that usually blocks work until it is fixed, but the steps above resolve it.

Official resources & further reading