{"record":{"id":"7465f3e1ea4bca5e","repo":"benbjohnson/litestream","slug":"replica-required-before-opening-database","errorCode":null,"errorMessage":"replica required before opening database","messagePattern":"replica required before opening database","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":782,"sourceCode":"\n\tdb.Logger.Info(\"database restored from backup\", \"path\", db.Path())\n\treturn nil\n}\n\n// Open initializes the background monitoring goroutine.\nfunc (db *DB) Open() (err error) {\n\tdb.mu.Lock()\n\tif db.opened {\n\t\tdb.mu.Unlock()\n\t\treturn nil // already open\n\t}\n\t// Recreate context for fresh start (handles reopen after close)\n\tdb.ctx, db.cancel = context.WithCancel(context.Background())\n\tdb.mu.Unlock()\n\n\t// Validate fields on database.\n\tif db.Replica == nil {\n\t\treturn fmt.Errorf(\"replica required before opening database\")\n\t}\n\tif db.Replica.Client == nil {\n\t\treturn fmt.Errorf(\"replica client required before opening database\")\n\t}\n\tif db.MinCheckpointPageN <= 0 {\n\t\treturn fmt.Errorf(\"minimum checkpoint page count required\")\n\t}\n\n\t// Clear old temporary files that my have been left from a crash.\n\tif err := removeTmpFiles(db.metaPath); err != nil {\n\t\treturn fmt.Errorf(\"cannot remove tmp files: %w\", err)\n\t}\n\n\t// Set the compactor client once before starting any goroutines.\n\tdb.compactor.VerifyCompaction = db.VerifyCompaction\n\tdb.compactor.RetentionEnabled = db.RetentionEnabled\n\tdb.compactor.client = db.Replica.Client\n","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L764-L800","documentation":"This error is returned by DB.Open (db.go) when the database object is opened without a Replica assigned. Litestream requires every database to have exactly one replication destination, so opening without one is a programming/configuration error rather than a runtime failure. It is a fail-fast validation performed at open time, before any file or network resources are touched.","triggerScenarios":"Constructing a litestream.DB struct programmatically (library API) and calling Open() without setting db.Replica, or building a DB from a config that has no replica section.","commonSituations":"Embedding Litestream in a Go application and forgetting to call db.NewReplica(...) / attach a replica client; a YAML config that defines a database path but omits the replica block; code paths that reopen a DB after Close and reconstruct it incorrectly.","solutions":["Set db.Replica (with a configured ReplicaClient) before calling Open().","If using a config file, add a replica section (e.g. s3, sftp, file) under the database.","Check the construction code for early returns that leave Replica nil, and reopen-after-close paths that rebuild the DB incompletely."],"exampleFix":"// before\ndb := litestream.NewDB(\"/data/app.db\")\nif err := db.Open(); err != nil { ... }\n// after\ndb := litestream.NewDB(\"/data/app.db\")\nclient := s3.NewReplicaClient()\nclient.Bucket = \"my-bucket\"\ndb.Replica = litestream.NewReplica(db, \"s3\")\ndb.Replica.Client = client\nif err := db.Open(); err != nil { ... }","handlingStrategy":"validation","validationCode":"if db.Replica == nil {\n    return fmt.Errorf(\"db %s: replica must be set before Open()\", db.Path())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build DBs through the config loader or a helper that wires Replica + Client together.","Add a startup assertion that every DB has a Replica before Open()."],"tags":["database","replication","validation","configuration"],"backgroundTag":"missing-required-argument","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}