benbjohnson/litestream · error
local position: %w
Error message
local position: %w
What it means
DB.SyncStatus() wraps any error returned by db.Pos() with the prefix "local position". db.Pos() reads the current WAL position (TXID) of the local database, so this means the local database's position could not be read — typically because the DB is not open, the WAL/SHM files are unreadable, or the underlying SQLite query failed. It is a wrapper, so the root cause is in the wrapped error.
Source
Thrown at db.go:699
// SyncStatus represents the current replication state of the database.
type SyncStatus struct {
LocalTXID ltx.TXID
RemoteTXID ltx.TXID
InSync bool
}
// SyncStatus returns the current replication status of the database, comparing
// the local transaction position against the remote replica position. The remote
// position is queried from the replica storage, so this method may perform I/O.
func (db *DB) SyncStatus(ctx context.Context) (SyncStatus, error) {
if db.Replica == nil {
return SyncStatus{}, fmt.Errorf("no replica configured")
}
localPos, err := db.Pos()
if err != nil {
return SyncStatus{}, fmt.Errorf("local position: %w", err)
}
remotePos, err := db.Replica.calcPos(ctx)
if err != nil {
return SyncStatus{}, fmt.Errorf("remote position: %w", err)
}
return SyncStatus{
LocalTXID: localPos.TXID,
RemoteTXID: remotePos.TXID,
InSync: localPos.TXID > 0 && localPos.TXID == remotePos.TXID,
}, nil
}
// SyncAndWait performs a full sync: WAL to LTX files, then LTX files to remote
// replica. Blocks until both stages complete.
func (db *DB) SyncAndWait(ctx context.Context) error {
if db.Replica == nil {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Inspect the wrapped %w error to find the root cause (it is chained, so errors.Is/errors.As work).
- Ensure db.Open(ctx) completed successfully before calling SyncStatus().
- Check that the database path exists and the process has read permissions on the db, WAL, and SHM files.
- If the local state is corrupted, consider litestream reset (or the reset API) to clear local LTX state and restore.
Example fix
// before
status, err := db.SyncStatus(ctx) // fails if DB not open
// after
if err := db.Open(ctx); err != nil {
return fmt.Errorf("open db: %w", err)
}
status, err := db.SyncStatus(ctx)
if err != nil {
return fmt.Errorf("sync status: %w", err) // inspect wrapped cause
} Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(db.Path()); err != nil {
// db file missing; expect position read failure
}
// ensure Open() has completed before calling SyncStatus Try / catch
status, err := db.SyncStatus(ctx)
if err != nil {
var root error
errors.As(err, &root) // or errors.Is on known causes
return fmt.Errorf("sync status unavailable: %w", err)
} Prevention
- Always call Open() before any status/sync API.
- Monitor disk space and WAL file health on the data volume.
- Run litestream under a user with read access to db, WAL, and SHM files.
- Use `litestream reset` deliberately when local LTX state is corrupt.
When it happens
Trigger: Calling db.SyncStatus(ctx) when db.Pos() fails: the DB was not opened (Open() not called or failed), the database file or WAL is corrupted/unreadable, or the SQLite connection used to read the position errors out.
Common situations: Status dashboards or health checks polling SyncStatus() before Open() finished; the database file was deleted or locked mid-run; disk I/O errors on the WAL file; calling SyncStatus on a DB struct constructed manually rather than via NewDB/Open.
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/5f39672e4d169bbf.
Report an issue: GitHub.