{"record":{"id":"fec9291519809efc","repo":"benbjohnson/litestream","slug":"open-persistent-hydration-file-w","errorCode":null,"errorMessage":"open persistent hydration file: %w","messagePattern":"open persistent hydration file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":689,"sourceCode":"\t\tpersistent: persistent,\n\t\tpageSize:   pageSize,\n\t\tclient:     client,\n\t\tlogger:     logger,\n\t}\n}\n\n// Init opens or creates the hydration file.\nfunc (h *Hydrator) Init() error {\n\tif err := os.MkdirAll(filepath.Dir(h.path), 0755); err != nil {\n\t\treturn fmt.Errorf(\"create hydration directory: %w\", err)\n\t}\n\n\tif h.persistent {\n\t\tif txid, err := h.loadMeta(); err == nil {\n\t\t\tif _, statErr := os.Stat(h.path); statErr == nil {\n\t\t\t\tfile, err := os.OpenFile(h.path, os.O_RDWR, 0600)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"open persistent hydration file: %w\", err)\n\t\t\t\t}\n\t\t\t\th.file = file\n\t\t\t\th.txid = txid\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tif err := os.Remove(h.metaPath()); err != nil && !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"remove stale hydration meta: %w\", err)\n\t\t}\n\t}\n\n\tfile, err := os.OpenFile(h.path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"create hydration file: %w\", err)\n\t}\n\th.file = file\n\treturn nil\n}","sourceCodeStart":671,"sourceCodeEnd":707,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L671-L707","documentation":"When the Hydrator is persistent and both a valid metadata file (with a txid) and an existing hydration file are found, Init reopens the hydration file with os.OpenFile(O_RDWR). This error wraps the failure to reopen that existing file. It means a prior hydration run left state on disk that now cannot be opened read-write.","triggerScenarios":"Init on a persistent hydrator where hydration.db exists and meta loads successfully, but os.OpenFile(path, O_RDWR, 0600) fails — file owned by another user, permission bits changed, or the file was replaced by a symlink/device with restrictive permissions.","commonSituations":"Previous run as root created the hydration file; current run as unprivileged user cannot open it read-write; backup/restore tooling altered file permissions; disk errors surfacing as open failures.","solutions":["Fix permissions on the existing hydration file so the process user can read/write it (chown/chmod 600).","Run all processes touching the hydration dir under the same user (avoid root-then-user switches).","Delete the stale hydration file and meta so the next Init recreates them, accepting a re-hydration.","Check for filesystem errors with dmesg/journalctl if permissions look correct."],"exampleFix":"// before\nsudo litestream ... // hydration file created as root:0600\n\n// after\nsudo chown app:app /var/lib/litestream/hydration/* # or run litestream always as app user","handlingStrategy":"validation","validationCode":"p := hydrator.Path()\nif fi, err := os.Stat(p); err == nil {\n    f, err := os.OpenFile(p, os.O_RDWR, 0)\n    if err != nil { /* fix perms or delete stale file before Init */ }\n    f.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := hydrator.Init(); err != nil && strings.Contains(err.Error(), \"open persistent hydration file\") {\n    // chown/chmod the file or remove it + meta to force re-hydration\n}","preventionTips":["Run every process touching the hydration dir as the same user.","Avoid sudo for one-off runs that create root-owned hydration files.","Backup tooling must preserve ownership/permissions (0600).","Monitor for stale hydration state after crashes and clean it deliberately."],"tags":["go","filesystem","hydration","file-open"],"backgroundTag":"file-open-failed","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"}