benbjohnson/litestream · critical

sync database %s: %w

Error message

sync database %s: %w

What it means

In one-shot (`-once`) mode, runOnce syncs each configured database (applying pending WAL changes and uploading LTX files) and returns the first error via named return. A failure from db.Sync — corrupted WAL, locked database, I/O error — is wrapped as "sync database <path>" so the failing database is identified.

Source

Thrown at cmd/litestream/replicate.go:403

		// Run one-shot replication in a goroutine so the caller can wait on execCh.
		go c.runOnce(ctx)
	}

	return nil
}

// runOnce performs one-shot replication for all databases.
// It syncs all databases, optionally takes snapshots, and enforces retention.
func (c *ReplicateCommand) runOnce(ctx context.Context) {
	var err error
	defer func() { c.execCh <- err }()

	for _, db := range c.Store.DBs() {
		slog.Info("syncing database", "path", db.Path())

		// Sync the database to process any pending WAL changes.
		if err = db.Sync(ctx); err != nil {
			err = fmt.Errorf("sync database %s: %w", db.Path(), err)
			return
		}

		// Sync the replica to upload any pending LTX files.
		if err = db.Replica.Sync(ctx); err != nil {
			err = fmt.Errorf("sync replica for %s: %w", db.Path(), err)
			return
		}

		// Force a snapshot if requested.
		if c.forceSnapshot {
			slog.Info("taking snapshot", "path", db.Path())
			if _, err = db.Snapshot(ctx); err != nil {
				err = fmt.Errorf("snapshot %s: %w", db.Path(), err)
				return
			}
		}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the wrapped inner error for the SQLite-level cause (lock, corruption, I/O)
  2. Retry the one-shot sync once the writer releases locks / quiesce the application
  3. If local LTX/WAL state is corrupted, run `litestream reset` for that database and re-sync
  4. Ensure litestream runs with read/write access to the DB, WAL, and SHM files

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-check DB accessibility before one-shot sync
[ -f "$DB" ] && [ -w "${DB}-wal" ] || echo "warn: wal missing/unwritable"
lsof "$DB" && echo "warn: database in use by another process"

Try / catch

if err := cmd.Run(ctx); err != nil {
    var dbErr error
    if strings.Contains(err.Error(), "sync database") {
        dbErr = err // path is embedded in message; unwrap for SQLite cause
        for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(e) {
            log.Printf("sqlite cause: %v", e)
        }
    }
    return err
}

Prevention

When it happens

Trigger: `db.Sync(ctx)` returns error during a `-once` run: the SQLite file is locked by another process, WAL is corrupted, disk is full, or the DB was modified concurrently in an unsupported way.

Common situations: Another process holds an exclusive lock during the one-shot backup; WAL/shm files out of sync after a crash; running litestream as a different user than the app writing the DB.

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/d21d58c99cbde53f. Report an issue: GitHub.