{"record":{"id":"6c1d56d383bfd665","repo":"tursodatabase/turso","slug":"8","errorCode":"8","errorMessage":"SQLite Error 8: 'attempt to write a readonly database'.","messagePattern":"SQLite Error 8: 'attempt to write a readonly database'\\.","errorType":"error_code","errorClass":"SqliteException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteBatch.cs","lineNumber":313,"sourceCode":"            {\n                using var command = new SqliteCommand(batchCommand.CommandText, connection, _transaction);\n                foreach (SqliteParameter parameter in batchCommand.Parameters)\n                    command.Parameters.Add(parameter);\n                command.ValidateManagedParameterValues();\n\n                var commandStatements = ManagedSqliteStatementParser.Parse(batchCommand.CommandText);\n                if (commandStatements.Count == 0)\n                    throw new InvalidOperationException(\"Batch command text must contain a SQL statement.\");\n                if (commandStatements.Any(static statement => statement.IsTransactionControl))\n                {\n                    throw new InvalidOperationException(\n                        \"Transaction-control SQL is not supported in a SqliteBatch. \"\n                        + \"Use SqliteConnection.BeginTransaction and SqliteTransaction instead.\");\n                }\n                if (connection.IsReadOnly\n                    && commandStatements.Any(SqliteCommand.IsWriteStatement))\n                {\n                    throw new SqliteException(\n                        Properties.Resources.SqliteNativeError(8, \"attempt to write a readonly database\"),\n                        8);\n                }\n\n                var firstManagedIndex = managedCommands.Count;\n                foreach (var statement in commandStatements)\n                {\n                    string sql;\n                    if (preparing)\n                    {\n                        sql = SqliteCommand.RewriteFacadeStatement(statement.Sql, connection);\n                    }\n                    else if (command.TryHandleFacadeStatement(statement.Sql, out sql))\n                    {\n                        continue;\n                    }\n\n                    using var managedCommand = command.CreateManagedCommand(statement, sql);","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteBatch.cs#L295-L331","documentation":"When the connection is opened in read-only mode (connection.IsReadOnly), BuildBatch simulates SQLite's SQLITE_READONLY error (code 8) for any batch containing a write statement (INSERT/UPDATE/DELETE/DDL detected by SqliteCommand.IsWriteStatement), failing the batch before anything is sent.","triggerScenarios":"Executing a SqliteBatch containing INSERT/UPDATE/DELETE/CREATE/DROP against a connection opened with read-only mode (e.g. Mode=ReadOnly connection string or read-only flag on remote/replica connections).","commonSituations":"Read-only replicas serving analytics queries where a maintenance DML script is accidentally run; connection string misconfiguration; wrong connection instance injected for a write path.","solutions":["Open the connection in read-write mode (remove Mode=ReadOnly / read-only flag) for batches containing writes","Split write statements out of read-only batches and run them on a writable connection","Check connection.IsReadOnly before building the batch and route accordingly"],"exampleFix":"// before\nvar conn = new SqliteConnection(\"...;Mode=ReadOnly\");\nawait batch.ExecuteReaderAsync(); // SqliteException code 8\n// after\nvar conn = new SqliteConnection(\"...\"); // read-write\nawait batch.ExecuteReaderAsync();","handlingStrategy":"validation","validationCode":"if (connection.IsReadOnly && SqliteCommand.IsWriteStatement(firstWriteStatement))\n    throw new InvalidOperationException(\"Cannot run write statements on a read-only connection\");","typeGuard":"static bool CanWriteOn(SqliteConnection c) => !c.IsReadOnly;","tryCatchPattern":"try { await batch.ExecuteReaderAsync(); }\ncatch (SqliteException ex) when (ex.SqliteErrorCode == 8) { /* reroute writes to a read-write connection */ }","preventionTips":["Check connection.IsReadOnly before building write batches","Maintain separate read-only and read-write connections and route by statement type","Audit connection strings for Mode=ReadOnly in write code paths"],"tags":["dotnet","batch","read-only","write","sqlite"],"backgroundTag":"permission-denied","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-09-06T07:15:26.990Z","contentChangedAt":"2026-09-06T07:15:26.990Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}