rqlite/rqlite · error
failed to get busy_timeout: %s
Error message
failed to get busy_timeout: %s
What it means
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.
Source
Thrown at db/db.go:776
// CheckpointTruncateWithTimeout performs a WAL checkpoint in TRUNCATE mode.
// The caller must guarantee that no write transactions will commit for the
// duration of the call. If all readers release their locks within dur,
// the WAL is truncated and nil is returned. If readers hold locks for
// the entire duration, an error is returned.
func (db *DB) CheckpointTruncateWithTimeout(dur time.Duration) (err error) {
start := time.Now()
defer func() {
if err != nil {
stats.Add(numCheckpointErrors, 1)
} else {
recordDuration(checkpointDuration, start)
stats.Add(numCheckpoints, 1)
}
}()
rwBt, _, err := db.BusyTimeout()
if err != nil {
return fmt.Errorf("failed to get busy_timeout: %s", err.Error())
}
if err := db.SetBusyTimeout(int(dur.Milliseconds()), -1); err != nil {
return fmt.Errorf("failed to set busy_timeout: %s", err.Error())
}
defer func() {
if err := db.SetBusyTimeout(rwBt, -1); err != nil {
db.logger.Printf("failed to reset busy_timeout: %s", err.Error())
}
}()
currMode, err := db.GetSynchronousMode()
if err != nil {
return fmt.Errorf("failed to get synchronous mode: %s", err.Error())
}
if err := db.SetSynchronousMode(SynchronousFull); err != nil {
return fmt.Errorf("failed to set synchronous mode to FULL: %s", err.Error())
}
defer func() {View on GitHub (pinned to 7586a4d1bd)
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.
Example fix
// before
db.Close()
db.CheckpointTruncateWithTimeout(5 * time.Second) // panics-free but errors
// after
if err := db.CheckpointTruncateWithTimeout(5 * time.Second); err != nil { ... }
db.Close() Defensive patterns
Strategy: try-catch
Try / catch
if err := database.CheckpointTruncateWithTimeout(5*time.Second); err != nil {
if strings.Contains(err.Error(), "failed to get busy_timeout") {
log.Printf("db connection unusable for checkpoint: %v", err)
}
} Prevention
- 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'
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to set busy_timeout: %s
- failed to set busy_timeout on checkpointing connection: %s
- failed to get synchronous mode: %s
- failed to set synchronous mode to FULL: %s
- failed to get busy_timeout on checkpointing connection: %s
AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03).
Data as JSON: /api/errors/8663f8da6f79cf93.
Report an issue: GitHub.