{"record":{"id":"c242f5824a708b28","repo":"benbjohnson/litestream","slug":"stat-database-w","errorCode":null,"errorMessage":"stat database: %w","messagePattern":"stat database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":744,"sourceCode":"\t}\n\treturn nil\n}\n\n// EnsureExists restores the database from the configured replica if the local\n// database file does not exist. If no backup is available, it returns nil and\n// a fresh database will be created on Open(). Must be called before Open().\nfunc (db *DB) EnsureExists(ctx context.Context) error {\n\tif db.Replica == nil {\n\t\treturn fmt.Errorf(\"no replica configured\")\n\t}\n\tif db.Replica.Client == nil {\n\t\treturn fmt.Errorf(\"no replica client configured\")\n\t}\n\n\tif _, err := os.Stat(db.Path()); err == nil {\n\t\treturn nil\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"stat database: %w\", err)\n\t}\n\n\tif dir := filepath.Dir(db.Path()); dir != \".\" {\n\t\tif err := os.MkdirAll(dir, 0o750); err != nil {\n\t\t\treturn fmt.Errorf(\"create parent directory: %w\", err)\n\t\t}\n\t}\n\n\topt := NewRestoreOptions()\n\topt.OutputPath = db.Path()\n\topt.IntegrityCheck = IntegrityCheckQuick\n\n\tif err := db.Replica.Restore(ctx, opt); err != nil {\n\t\tif errors.Is(err, ErrTxNotAvailable) || errors.Is(err, ErrNoSnapshots) {\n\t\t\tdb.Logger.Debug(\"no backup found, will create fresh database\")\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"restore from backup: %w\", err)","sourceCodeStart":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L726-L762","documentation":"DB.EnsureExists(ctx) stats db.Path() to decide whether the local database file exists. If os.Stat fails with any error other than \"not exist\" (e.g. permission denied on a parent directory, I/O error, or path is not accessible), litestream wraps it as \"stat database\". It does NOT occur when the file is simply missing — that is the normal restore path.","triggerScenarios":"os.Stat(db.Path()) returning e.g. EACCES, ELOOP, or ENOTDIR: a parent directory lacks execute/search permission, the path contains a bad symlink, or a path component is a file rather than a directory.","commonSituations":"Running litestream under a service user that cannot traverse /var/lib/appdata; a symlink loop after a bad migration; db path accidentally pointing inside a file (e.g. /data/db.sqlite/nested); SELinux/AppArmor blocking access.","solutions":["Read the wrapped errno to identify the exact stat failure.","Check execute (x) permission on every parent directory of the db path for the litestream user.","Verify the db path components: no symlink loops, no file-where-directory-expected.","Fix ownership/permissions (chown/chmod) or adjust the db path in config; check LSM (SELinux/AppArmor) denials in audit logs."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if _, err := os.Stat(db.Path()); err != nil && !os.IsNotExist(err) {\n    return fmt.Errorf(\"db path inaccessible before EnsureExists: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := db.EnsureExists(ctx); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) && pe.Op == \"stat\" {\n        log.Error(\"cannot access db path\", \"path\", pe.Path, \"err\", pe.Err)\n    }\n    return err\n}","preventionTips":["Verify the litestream user has execute permission on all parent directories.","Avoid symlink loops; use plain directories for data paths.","Check SELinux/AppArmor policies when running under a hardened OS.","Smoke-test os.Stat on the configured path in deployment checks."],"tags":["filesystem","permissions","error-wrapping"],"backgroundTag":"permission-denied","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"}