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

  1. Ensure the DB is open and Close() hasn't been called before invoking checkpoint operations.
  2. Check the wrapped error: 'sql: database is closed' means lifecycle mismanagement; 'bad connection' means reconnect/reopen the DB.
  3. 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

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

Related errors


AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03). Data as JSON: /api/errors/8663f8da6f79cf93. Report an issue: GitHub.