{"record":{"id":"8663f8da6f79cf93","repo":"rqlite/rqlite","slug":"failed-to-get-busy-timeout-s","errorCode":null,"errorMessage":"failed to get busy_timeout: %s","messagePattern":"failed to get busy_timeout: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db/db.go","lineNumber":776,"sourceCode":"// CheckpointTruncateWithTimeout performs a WAL checkpoint in TRUNCATE mode.\n// The caller must guarantee that no write transactions will commit for the\n// duration of the call. If all readers release their locks within dur,\n// the WAL is truncated and nil is returned. If readers hold locks for\n// the entire duration, an error is returned.\nfunc (db *DB) CheckpointTruncateWithTimeout(dur time.Duration) (err error) {\n\tstart := time.Now()\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tstats.Add(numCheckpointErrors, 1)\n\t\t} else {\n\t\t\trecordDuration(checkpointDuration, start)\n\t\t\tstats.Add(numCheckpoints, 1)\n\t\t}\n\t}()\n\n\trwBt, _, err := db.BusyTimeout()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get busy_timeout: %s\", err.Error())\n\t}\n\tif err := db.SetBusyTimeout(int(dur.Milliseconds()), -1); err != nil {\n\t\treturn fmt.Errorf(\"failed to set busy_timeout: %s\", err.Error())\n\t}\n\tdefer func() {\n\t\tif err := db.SetBusyTimeout(rwBt, -1); err != nil {\n\t\t\tdb.logger.Printf(\"failed to reset busy_timeout: %s\", err.Error())\n\t\t}\n\t}()\n\n\tcurrMode, err := db.GetSynchronousMode()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to get synchronous mode: %s\", err.Error())\n\t}\n\tif err := db.SetSynchronousMode(SynchronousFull); err != nil {\n\t\treturn fmt.Errorf(\"failed to set synchronous mode to FULL: %s\", err.Error())\n\t}\n\tdefer func() {","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/rqlite/rqlite/blob/7586a4d1bdbd9a5a80021664c5a863cd850adb60/db/db.go#L758-L794","documentation":"CheckpointTruncateWithTimeout first reads the current busy_timeout so it can restore it afterwards; failure is wrapped as \"failed to get busy_timeout: %s\". BusyTimeout() reads PRAGMA busy_timeout from the RW connection, so this means the RW connection could not execute that PRAGMA — i.e. the connection is broken or closed.","triggerScenarios":"Calling CheckpointTruncateWithTimeout (or Checkpoint with a timeout) on a DB whose read-write connection has been closed, failed, or is otherwise unable to run PRAGMA busy_timeout.","commonSituations":"Calling checkpoint APIs after DB.Close(); a connection invalidated by prior I/O errors or corruption; using a DB handle concurrently from another goroutine that closed it.","solutions":["Ensure the DB is open and Close() hasn't been called before invoking checkpoint operations.","Check the wrapped error: 'sql: database is closed' means lifecycle mismanagement; 'bad connection' means reconnect/reopen the DB.","Guard against data races — don't close the DB from another goroutine while checkpointing."],"exampleFix":"// before\ndb.Close()\ndb.CheckpointTruncateWithTimeout(5 * time.Second) // panics-free but errors\n// after\nif err := db.CheckpointTruncateWithTimeout(5 * time.Second); err != nil { ... }\ndb.Close()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := database.CheckpointTruncateWithTimeout(5*time.Second); err != nil {\n    if strings.Contains(err.Error(), \"failed to get busy_timeout\") {\n        log.Printf(\"db connection unusable for checkpoint: %v\", err)\n    }\n}","preventionTips":["Sequence all Close() calls after checkpoint operations complete","Guard DB close with sync.Once to avoid concurrent shutdown races","Reopen the DB if wrapped error indicates 'bad connection'"],"tags":["sqlite","pragma","busy-timeout","checkpoint"],"backgroundTag":"database-connection-closed","analyzedSha":"7586a4d1bdbd9a5a80021664c5a863cd850adb60","analyzedAt":"2026-09-03T07:03:02.260Z","contentChangedAt":"2026-09-03T07:03:02.260Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}