benbjohnson/litestream · error

read position before sync: %w

Error message

read position before sync: %w

What it means

Store.SyncDB failed before performing the sync because it could not read the database's current replication position via db.MaxLTX(). The wrapped error (ErrDatabaseNotOpen, storage errors, or internal position-lookup failures) is preserved with %w. SyncDB reports this position so callers can compare before/after TXIDs, so any failure reading it aborts the whole call.

Source

Thrown at store.go:445

// SyncDB forces an immediate sync for a database. If wait is true, blocks
// until both WAL-to-LTX and LTX-to-remote sync complete. If wait is false,
// only performs the WAL-to-LTX sync and lets the replica monitor handle upload.
// Lock waits are context-aware: the timeout is honored while waiting for
// the database sync executor and the replica sync lock.
func (s *Store) SyncDB(ctx context.Context, path string, wait bool) (SyncDBResult, error) {
	db := s.FindDB(path)
	if db == nil {
		return SyncDBResult{}, fmt.Errorf("%w: %s", ErrDatabaseNotFound, path)
	}

	if !db.IsOpen() {
		return SyncDBResult{}, fmt.Errorf("%w: %s", ErrDatabaseNotOpen, path)
	}

	_, beforeTXID, err := db.MaxLTX()
	if err != nil {
		return SyncDBResult{}, fmt.Errorf("read position before sync: %w", err)
	}

	if wait {
		if err := db.SyncAndWait(ctx); err != nil {
			return SyncDBResult{}, fmt.Errorf("sync database: %w", err)
		}
	} else {
		if err := db.Sync(ctx); err != nil {
			return SyncDBResult{}, fmt.Errorf("sync database: %w", err)
		}
	}

	_, afterTXID, err := db.MaxLTX()
	if err != nil {
		return SyncDBResult{}, fmt.Errorf("read position after sync: %w", err)
	}

	var replicatedTXID uint64

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify the database path passed to SyncDB exactly matches a path registered with the Store (check Store output of running DBs).
  2. Call SyncDB only after the Store has fully started and opened the database; wait for startup or use the Store's DB lookup API.
  3. Check that the database was not closed/unregistered concurrently; serialize calls or hold a reference to the *DB.
  4. Inspect the wrapped error (errors.Is(err, ErrDatabaseNotOpen)) to distinguish not-open from an internal MaxLTX failure.

Example fix

// before
res, err := store.SyncDB(ctx, "/data/mydb.sqlite", true)
// after
if err := store.OpenDatabase(ctx, "/data/mydb.sqlite"); err != nil { return err }
res, err := store.SyncDB(ctx, "/data/mydb.sqlite", true)
if err != nil && errors.Is(err, litestream.ErrDatabaseNotOpen) {
    // path not open: look up correct path
}
Defensive patterns

Strategy: validation

Validate before calling

if store == nil || path == "" {
    return fmt.Errorf("store and db path required")
}
// look up DB in store before syncing
if _, err := store.DB(context.Background(), path); err != nil {
    return fmt.Errorf("db %q not registered: %w", path, err)
}

Type guard

func isOpen(store *litestream.Store, path string) bool {
    db, err := store.DB(context.Background(), path)
    return err == nil && db != nil && db.SyncStatus() != litestream.SyncStatusUnknown
}

Try / catch

res, err := store.SyncDB(ctx, path, true)
if err != nil {
    if errors.Is(err, litestream.ErrDatabaseNotOpen) {
        return fmt.Errorf("db %q is not open: register it with the store first", path)
    }
    return err
}

Prevention

When it happens

Trigger: Store.SyncDB(ctx, path, wait) is called with a path that is not registered/open in the Store (db.IsOpen() false -> ErrDatabaseNotOpen), or MaxLTX() fails while consulting the local LTX position metadata.

Common situations: Calling SyncDB with a database path that was never registered via OpenDB/Config, after the DB was closed or removed from the store, a typo in the path, or racing SyncDB against Store shutdown.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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