{"record":{"id":"82c2e1b11b60141d","repo":"benbjohnson/litestream","slug":"begin-transaction-w","errorCode":null,"errorMessage":"begin transaction: %w","messagePattern":"begin transaction: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/litestream-test/populate.go","lineNumber":177,"sourceCode":"\tduration := time.Since(startTime)\n\tfinalSize, _ := getDatabaseSize(c.DB)\n\n\tslog.Info(\"Population complete\",\n\t\t\"duration\", duration,\n\t\t\"final_size_mb\", finalSize/1024/1024,\n\t\t\"throughput_mb_per_sec\", fmt.Sprintf(\"%.2f\", float64(finalSize)/1024/1024/duration.Seconds()),\n\t)\n\n\treturn nil\n}\n\nfunc (c *PopulateCommand) populateTable(ctx context.Context, db *sql.DB, tableName string, rowCount int) error {\n\tdata := make([]byte, c.RowSize)\n\n\tfor i := 0; i < rowCount; i += c.BatchSize {\n\t\ttx, err := db.Begin()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"begin transaction: %w\", err)\n\t\t}\n\n\t\tstmt, err := tx.Prepare(fmt.Sprintf(`\n\t\t\tINSERT INTO %s (data, text_field, int_field, float_field, timestamp)\n\t\t\tVALUES (?, ?, ?, ?, ?)\n\t\t`, tableName))\n\t\tif err != nil {\n\t\t\ttx.Rollback()\n\t\t\treturn fmt.Errorf(\"prepare statement: %w\", err)\n\t\t}\n\n\t\tbatchEnd := i + c.BatchSize\n\t\tif batchEnd > rowCount {\n\t\t\tbatchEnd = rowCount\n\t\t}\n\n\t\tfor j := i; j < batchEnd; j++ {\n\t\t\tcryptorand.Read(data)","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/cmd/litestream-test/populate.go#L159-L195","documentation":"populateTable wraps a failed db.Begin() in this error. Rows are inserted in batches of BatchSize, each batch wrapped in a transaction for throughput. If a transaction cannot be started, no batch is inserted and population aborts.","triggerScenarios":"db.Begin() returns an error: the connection pool has no usable connection (driver issue), the database is locked by another writer exceeding the busy timeout, or the database is in a broken state after an I/O error.","commonSituations":"A concurrent litestream or backup process holding the write lock; running with database_sqlite driver connection limits exhausted; a prior crashed run leaving a hot journal that cannot be recovered.","solutions":["Check the wrapped SQLite error for SQLITE_BUSY and increase the busy timeout in the DSN (e.g. ?_pragma=busy_timeout(5000)).","Stop competing writers (other litestream-test runs, monitoring connections) during population.","Verify the database file and hot journal are writable and not corrupted.","Retry the run; SQLITE_BUSY is often transient under contention."],"exampleFix":"// before\ndb, _ := sql.Open(\"sqlite\", c.DB)\n// after\ndb, _ := sql.Open(\"sqlite\", c.DB+\"?_pragma=busy_timeout(5000)\")","handlingStrategy":"retry","validationCode":"// increase busy timeout so concurrent locks don't fail Begin\ndb, err := sql.Open(\"sqlite\", c.DB+\"?_pragma=busy_timeout(10000)\")\nif err != nil {\n    return err\n}","typeGuard":null,"tryCatchPattern":"tx, err := db.Begin()\nif err != nil {\n    if strings.Contains(err.Error(), \"SQLITE_BUSY\") {\n        time.Sleep(250 * time.Millisecond)\n        tx, err = db.Begin() // bounded retry\n    }\n    if err != nil {\n        return fmt.Errorf(\"begin transaction: %w\", err)\n    }\n}","preventionTips":["Set a busy_timeout in the DSN for every test run.","Ensure a single writer process per database.","Check for stale hot journals after crashed runs.","Ping the DB (db.Ping) before starting population to validate connectivity."],"tags":["sqlite","transaction","database"],"backgroundTag":"database-write-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}