{"record":{"id":"5f39672e4d169bbf","repo":"benbjohnson/litestream","slug":"local-position-w","errorCode":null,"errorMessage":"local position: %w","messagePattern":"local position: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":699,"sourceCode":"\n// SyncStatus represents the current replication state of the database.\ntype SyncStatus struct {\n\tLocalTXID  ltx.TXID\n\tRemoteTXID ltx.TXID\n\tInSync     bool\n}\n\n// SyncStatus returns the current replication status of the database, comparing\n// the local transaction position against the remote replica position. The remote\n// position is queried from the replica storage, so this method may perform I/O.\nfunc (db *DB) SyncStatus(ctx context.Context) (SyncStatus, error) {\n\tif db.Replica == nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"no replica configured\")\n\t}\n\n\tlocalPos, err := db.Pos()\n\tif err != nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"local position: %w\", err)\n\t}\n\n\tremotePos, err := db.Replica.calcPos(ctx)\n\tif err != nil {\n\t\treturn SyncStatus{}, fmt.Errorf(\"remote position: %w\", err)\n\t}\n\n\treturn SyncStatus{\n\t\tLocalTXID:  localPos.TXID,\n\t\tRemoteTXID: remotePos.TXID,\n\t\tInSync:     localPos.TXID > 0 && localPos.TXID == remotePos.TXID,\n\t}, nil\n}\n\n// SyncAndWait performs a full sync: WAL to LTX files, then LTX files to remote\n// replica. Blocks until both stages complete.\nfunc (db *DB) SyncAndWait(ctx context.Context) error {\n\tif db.Replica == nil {","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L681-L717","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nstatus, err := db.SyncStatus(ctx) // fails if DB not open\n// after\nif err := db.Open(ctx); err != nil {\n    return fmt.Errorf(\"open db: %w\", err)\n}\nstatus, err := db.SyncStatus(ctx)\nif err != nil {\n    return fmt.Errorf(\"sync status: %w\", err) // inspect wrapped cause\n}","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(db.Path()); err != nil {\n    // db file missing; expect position read failure\n}\n// ensure Open() has completed before calling SyncStatus","typeGuard":null,"tryCatchPattern":"status, err := db.SyncStatus(ctx)\nif err != nil {\n    var root error\n    errors.As(err, &root) // or errors.Is on known causes\n    return fmt.Errorf(\"sync status unavailable: %w\", err)\n}","preventionTips":["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."],"tags":["database","sqlite","error-wrapping","position"],"backgroundTag":"database-query-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}