benbjohnson/litestream · error

begin passive checkpoint barrier: %w

Error message

begin passive checkpoint barrier: %w

What it means

For passive checkpoints, Litestream opens a barrier transaction (BeginTx) on the SQLite database to hold a read lock during the checkpoint. This error wraps a failure to begin that transaction, so the passive checkpoint cannot proceed safely.

Source

Thrown at db.go:2495

	}

	// Copy end of WAL before checkpoint to copy as much as possible.
	db.setSyncDiagPhase(diagPhaseCheckpointCopyBefore,
		func(s *diagState) {
			s.checkpointMode = mode
			s.lastSyncedWALOffset = exec.state.lastSyncedWALOffset
		})
	result, err := db.verifyAndSyncWithExecutor(ctx, true, exec, 0)
	if err != nil {
		return false, fmt.Errorf("cannot copy wal before checkpoint: %w", err)
	}
	exec.applySyncResult(result)

	var barrierTx *sql.Tx
	if mode == CheckpointModePassive {
		barrierTx, err = db.db.BeginTx(ctx, nil)
		if err != nil {
			return false, fmt.Errorf("begin passive checkpoint barrier: %w", err)
		}
		defer func() {
			if barrierTx != nil {
				_ = rollback(barrierTx)
			}
		}()

		if _, err := barrierTx.ExecContext(ctx, `INSERT INTO _litestream_lock (id) VALUES (1);`); err != nil {
			return false, fmt.Errorf("_litestream_lock: %w", err)
		}

		result, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)
		if err != nil {
			return false, fmt.Errorf("cannot seal wal before passive checkpoint: %w", err)
		}
		exec.applySyncResult(result)
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Identify blocking writers (`PRAGMA busy_timeout;`, `sqlite3 app.db '.timeout'` or lsof on the db) and let them finish
  2. Increase the busy timeout / reduce long-running write transactions in the application
  3. Avoid forcing checkpoints during application write bursts; rely on the normal checkpoint cadence
  4. Retry after the lock clears — the error is typically transient

Example fix

// before
app: single long transaction holding write lock for minutes
litestream: begin passive checkpoint barrier: database is locked
// after
-- break long transactions into smaller commits
-- and/or set busy_timeout
PRAGMA busy_timeout = 5000;
Defensive patterns

Strategy: try-catch

Validate before calling

// avoid forcing passive checkpoints while heavy writers run
if longWriteTxActive(app) { log.Printf("defer checkpoint") }

Try / catch

// go
if err := checkpoint(ctx, CheckpointModePassive); err != nil {
    if strings.Contains(err.Error(), "begin passive checkpoint barrier") {
        // database locked/closed: retry with backoff; check for blocking writers
    }
}

Prevention

When it happens

Trigger: In checkpointWithExecutor, mode == CheckpointModePassive, when db.db.BeginTx(ctx, nil) returns an error — database locked beyond busy timeout, database closed, or context canceled.

Common situations: Another process holds a long write transaction (locking the DB), the database is in a locking dispute with other tooling, or Litestream is shutting down (context canceled) when the checkpoint fires.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/bed8f96070dfd82c. Report an issue: GitHub.