{"record":{"id":"a65fa2d4267c29fc","repo":"n8n-io/n8n","slug":"timeout-waiting-for-lock-sqlitewriteconnectionmute","errorCode":null,"errorMessage":"Timeout waiting for lock SqliteWriteConnectionMutex to become available","messagePattern":"Timeout waiting for lock SqliteWriteConnectionMutex to become available","errorType":"exception","errorClass":"LockAcquireTimeoutError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/driver/sqlite-pooled/SqliteWriteConnection.ts","lineNumber":186,"sourceCode":"\tprivate assertNotReleased() {\n\t\tif (this.isReleased) {\n\t\t\tthrow new DriverAlreadyReleasedError();\n\t\t}\n\t}\n\n\tprivate captureInvariantViolated(extra: Record<string, string | boolean>) {\n\t\tconst error = new InvariantViolatedError();\n\t\tconsole.error(\n\t\t\t'Invariant violated:',\n\t\t\tObject.keys(extra)\n\t\t\t\t.map((key) => `${key}=${extra[key]}`)\n\t\t\t\t.join(', '),\n\t\t);\n\t\tconsole.error(error);\n\t}\n\n\tprivate throwLockTimeoutError(cause: Error) {\n\t\tthrow new LockAcquireTimeoutError('SqliteWriteConnectionMutex', {\n\t\t\tcause,\n\t\t});\n\t}\n}\n","sourceCodeStart":168,"sourceCodeEnd":191,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/driver/sqlite-pooled/SqliteWriteConnection.ts#L168-L191","documentation":"Thrown by SqliteWriteConnection when its single-writer async-mutex cannot be acquired within the configured acquireTimeout (set from the pool's acquireTimeout option). SQLite permits only one writer at a time; n8n's pooled driver serializes writes behind this mutex. The underlying async-mutex returns the E_TIMEOUT sentinel, which the driver rewrites into LockAcquireTimeoutError and attaches as the cause. The message names the specific lock ('SqliteWriteConnectionMutex').","triggerScenarios":"Calling any write path (INSERT/UPDATE/DELETE, migrations, schema changes) on the SQLite pooled driver while a previous write holds the mutex longer than acquireTimeout. Concretely: a long-running migration, a multi-thousand-row transaction, a query that scans a large table without an index, or a deadlock between an unfinished runExclusive callback and the pool's acquire timeout. Also fires when close() races with an in-flight write and cancel() rejects waiters.","commonSituations":"Default SQLite deployments of n8n (small/self-hosted) under load spikes; a manual migration on a large database; a workflow that fires many concurrent executions; another process holding the DB file lock (e.g. two n8n instances pointed at the same sqlite file, or an external sqlite3 session in WAL-interactive mode); slow disk/FS (NFS, network volumes) inflating write latency.","solutions":["Raise the pool acquireTimeout in the datasource config (DB_SQLITE_POOL_ACQUIRE_TIMEOUT or the relevant @n8n/typeorm sqlite pool option) so the longest legitimate write finishes inside the window.","Find the slow writer: enable DB query logging or DB profiling and look for the write/migration that exceeds the timeout; add missing indexes or batch the operation.","Ensure only one n8n process uses the SQLite file (SQLite is single-writer by design); move to PostgreSQL for multi-instance or high-write deployments.","If running on a network/shared filesystem, move the sqlite file to local disk, or switch to Postgres.","Confirm the write transaction is being committed/released (no leaked runExclusive callbacks, no un-awaited transactions)."],"exampleFix":"// before - default timeout too short for large migrations\nconst ds = new DataSource({\n  type: 'sqlite',\n  database: 'n8n.sqlite',\n  // pool acquireTimeout defaulting to ~5s, large writes time out\n});\n\n// after - raise the SQLite pool acquire timeout\nconst ds = new DataSource({\n  type: 'sqlite',\n  database: 'n8n.sqlite',\n  poolSize: 1,\n  acquireTimeout: 60_000, // 60s window for the longest write\n});","handlingStrategy":"retry","validationCode":"// Before issuing the write, confirm no other long writer is in flight\n// by checking process state; raise acquireTimeout to a safe bound.\nconst acquireTimeout = Math.max(\n  config.DB_SQLITE_POOL_ACQUIRE_TIMEOUT ?? 5_000,\n  estimatedLongestWriteMs * 2,\n);\nconst ds = new DataSource({ type: 'sqlite', database, acquireTimeout });","typeGuard":"function isLockAcquireTimeoutError(e: unknown): e is import('@n8n/typeorm/error/LockAcquireTimeoutError').LockAcquireTimeoutError {\n  return e instanceof Error && /Timeout waiting for lock SqliteWriteConnectionMutex/.test(e.message);\n}","tryCatchPattern":"try {\n  await writeRepo.save(batch);\n} catch (e) {\n  if (isLockAcquireTimeoutError(e)) {\n    // transient contention — back off and retry, then surface if still failing\n    await backoffRetry(() => writeRepo.save(batch), { retries: 3, baseMs: 200 });\n    return;\n  }\n  throw e;\n}","preventionTips":["Run a single n8n/TypeORM process per SQLite file; use Postgres for multi-instance.","Tune acquireTimeout to at least 2x your slowest legitimate write.","Keep write transactions short — batch but commit frequently.","Monitor for slow queries/migrations and index hot tables.","Move the SQLite file to local disk, not NFS/network shares."],"tags":["sqlite","concurrency","locking","timeout","database"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}