{"record":{"id":"2e86e4636aeccedd","repo":"tursodatabase/turso","slug":"5","errorCode":"5","errorMessage":"SQLite Error 5: 'database is locked'.","messagePattern":"SQLite Error 5: 'database is locked'\\.","errorType":"exception","errorClass":"SqliteException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs","lineNumber":289,"sourceCode":"        EnsureExecutable(method);\n        if (Connection!.IsManagedConnection)\n        {\n            return ExecuteManagedAsync(method, behavior, CancellationToken.None)\n                .GetAwaiter()\n                .GetResult();\n        }\n\n        if (IsEmptyCommand(CommandText))\n        {\n            _hasOpenReader = true;\n            Connection?.ReaderOpened();\n            return new SqliteDataReader(this, -1, behavior, CloseReader);\n        }\n\n        if (Connection?.HasOpenReader == true && IsWriteCommand(CommandText))\n        {\n            Thread.Sleep(TimeSpan.FromSeconds(CommandTimeout));\n            throw new SqliteException(Properties.Resources.SqliteNativeError(5, \"database is locked\"), 5);\n        }\n        if (Connection?.IsReadOnly == true && IsWriteCommand(CommandText))\n            throw new SqliteException(Properties.Resources.SqliteNativeError(8, \"attempt to write a readonly database\"), 8);\n\n        var recordsAffected = 0;\n        var statements = SplitStatements(CommandText);\n        try\n        {\n            for (var i = 0; i < statements.Count; i++)\n            {\n                if (TryHandleFacadeStatement(statements[i], out var sql))\n                    continue;\n\n                var statement = PrepareSingleStatement(sql);\n                if (TursoBindings.GetFieldCount(statement) > 0)\n                {\n                    _hasOpenReader = true;\n                    Connection?.ReaderOpened();","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteCommand.cs#L271-L307","documentation":"The provider emulates SQLITE_BUSY for single-connection interleaving: if the SqliteConnection already has an open DataReader and the incoming command looks like a write (IsWriteCommand), Execute sleeps for CommandTimeout seconds and then throws SqliteException code 5 'database is locked'. A live reader pins a snapshot of rows a write would mutate, so write-while-reading on one connection is refused instead of corrupting iteration.","triggerScenarios":"Executing a write command on the same connection inside a using (var reader = ...) loop that is still open; issuing UPDATE/INSERT/DELETE from a callback while enumerating results; a reader left undisposed on an early-return path before a later write runs.","commonSituations":"Read-then-update loops (load rows, mutate, save) on one connection; progress callbacks writing during enumeration; forgotten reader disposal; large CommandTimeout values that make the failure slow as well as wrong.","solutions":["Fully read and dispose the DataReader before executing any write - materialize results into a list first.","Buffer the rows to change, close the reader, then run one batched write.","If writes must interleave with reads, use a second SqliteConnection for them.","Keep CommandTimeout small so a design mistake fails fast instead of sleeping for the full timeout."],"exampleFix":"// before\nusing (var reader = selectCmd.ExecuteReader()) {\n    while (reader.Read()) {\n        updateCmd.ExecuteNonQuery(); // same connection, reader open -> code 5\n    }\n}\n\n// after\nvar ids = new List<long>();\nusing (var reader = selectCmd.ExecuteReader())\n    while (reader.Read()) ids.Add(reader.GetInt64(0)); // reader disposed on exit\n\nforeach (var id in ids)\n    ExecuteUpdate(conn, id);","handlingStrategy":"retry","validationCode":"// Materialize reads before writing on the same connection\nvar rows = new List<Row>();\nusing (var r = selectCmd.ExecuteReader())\n    while (r.Read()) rows.Add(MapRow(r));\n// reader is disposed here -> safe to write\nforeach (var row in rows) WriteRow(conn, row);","typeGuard":null,"tryCatchPattern":"try {\n    updateCmd.ExecuteNonQuery();\n} catch (SqliteException ex) when (ex.SqliteErrorCode == 5) {\n    // dispose any open DataReaders on this connection, then retry once\n    reader.Dispose();\n    updateCmd.ExecuteNonQuery();\n}","preventionTips":["Scope DataReaders tightly with using; never leave one open across writes.","Materialize query results, then write.","Set CommandTimeout low so misdesigned paths fail fast.","Use a dedicated connection for interleaved writes."],"tags":["dotnet","sqlite","database-locked","datareader"],"backgroundTag":"database-locked","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}