{"record":{"id":"6d81b1dccdc32cd6","repo":"benbjohnson/litestream","slug":"w-s-6d81b1","errorCode":null,"errorMessage":"%w: %s","messagePattern":"%w: %s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store.go","lineNumber":436,"sourceCode":"\treturn nil\n}\n\n// SyncDBResult holds the result of a sync operation.\ntype SyncDBResult struct {\n\tTXID           uint64\n\tReplicatedTXID uint64\n\tChanged        bool\n}\n\n// SyncDB forces an immediate sync for a database. If wait is true, blocks\n// until both WAL-to-LTX and LTX-to-remote sync complete. If wait is false,\n// only performs the WAL-to-LTX sync and lets the replica monitor handle upload.\n// Lock waits are context-aware: the timeout is honored while waiting for\n// the database sync executor and the replica sync lock.\nfunc (s *Store) SyncDB(ctx context.Context, path string, wait bool) (SyncDBResult, error) {\n\tdb := s.FindDB(path)\n\tif db == nil {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"%w: %s\", ErrDatabaseNotFound, path)\n\t}\n\n\tif !db.IsOpen() {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"%w: %s\", ErrDatabaseNotOpen, path)\n\t}\n\n\t_, beforeTXID, err := db.MaxLTX()\n\tif err != nil {\n\t\treturn SyncDBResult{}, fmt.Errorf(\"read position before sync: %w\", err)\n\t}\n\n\tif wait {\n\t\tif err := db.SyncAndWait(ctx); err != nil {\n\t\t\treturn SyncDBResult{}, fmt.Errorf(\"sync database: %w\", err)\n\t\t}\n\t} else {\n\t\tif err := db.Sync(ctx); err != nil {\n\t\t\treturn SyncDBResult{}, fmt.Errorf(\"sync database: %w\", err)","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/store.go#L418-L454","documentation":"SyncDB resolves the database by path and returns a wrapped sentinel: fmt.Errorf(\"%w: %s\", ErrDatabaseNotFound, path). Unlike EnableDB/DisableDB, this uses a sentinel error so callers can match it with errors.Is. It means no registered DB corresponds to the given path.","triggerScenarios":"Store.SyncDB(ctx, path, wait) (IPC handleSync) with a path not present in the Store's registry.","commonSituations":"IPC sync requests using a path spelling that differs from the configured one (relative path, symlink, trailing slash); syncing after the DB was unregistered or removed during a config reload.","solutions":["Check errors.Is(err, ErrDatabaseNotFound) to distinguish lookup failure from sync failure","Resolve and pass the exact registered path (filepath.Abs/Clean, same as config)","Query the store/IPC for registered DB paths before issuing a sync","Register the database first if it is expected to exist"],"exampleFix":"// before\n_, err := store.SyncDB(ctx, \"app.db\", true) // sentinel lost on generic match\n// after\nabs, _ := filepath.Abs(\"app.db\")\nif _, err := store.SyncDB(ctx, abs, true); err != nil {\n    if errors.Is(err, ErrDatabaseNotFound) { /* handle missing db */ }\n}","handlingStrategy":"validation","validationCode":"abs, err := filepath.Abs(path)\nif err != nil { return err }\nif store.FindDB(abs) == nil {\n    return fmt.Errorf(\"db not registered: %s\", abs)\n}","typeGuard":null,"tryCatchPattern":"if _, err := store.SyncDB(ctx, abs, wait); err != nil {\n    if errors.Is(err, ErrDatabaseNotFound) {\n        // lookup failure: fix path or register db\n    } else if errors.Is(err, ErrDatabaseNotOpen) {\n        // enable first\n    }\n    return err\n}","preventionTips":["Always match SyncDB errors with errors.Is against the exported sentinels","Normalize paths before calling store APIs","Keep config, registration, and IPC paths identical"],"tags":["database","not-found","sentinel-error"],"backgroundTag":"resource-not-found","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"}