{"record":{"id":"4717acafa7a9befa","repo":"benbjohnson/litestream","slug":"create-hydration-file-w","errorCode":null,"errorMessage":"create hydration file: %w","messagePattern":"create hydration file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":703,"sourceCode":"\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}\n\n// Complete returns true if hydration has completed.\nfunc (h *Hydrator) Complete() bool {\n\treturn h.complete.Load()\n}\n\n// SetComplete marks hydration as complete.\nfunc (h *Hydrator) SetComplete() {\n\th.complete.Store(true)\n}\n\n// Disable temporarily disables hydrated reads (used during time travel).\nfunc (h *Hydrator) Disable() {\n\th.complete.Store(false)","sourceCodeStart":685,"sourceCodeEnd":721,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L685-L721","documentation":"After any stale state is cleared, Hydrator.Init creates/opens the hydration database file with os.OpenFile(path, O_RDWR|O_CREATE|O_TRUNC, 0600). This error wraps that failure. It is the final and most common step of hydration initialization, so any file-creation problem in the hydration directory surfaces here.","triggerScenarios":"Init on a fresh or cleared hydrator where the hydration file cannot be created — directory permissions deny write, disk full, path exceeds NAME_MAX, or the path exists as a directory.","commonSituations":"Disk-full on the volume holding the hydration path; hydrationPath accidentally pointing at a directory; SELinux denials on file creation; tiny ephemeral volumes filling up during hydration of large databases.","solutions":["Check free space on the filesystem of the hydration path (df -h) and free or expand it.","Ensure the hydration directory is writable by the process user and that hydrationPath names a file, not a directory.","Confirm no MAC policy (SELinux/AppArmor) blocks file creation there; check audit logs.","Point hydrationPath at a dedicated writable volume sized for the database."],"exampleFix":"// before\nhydrator := NewHydrator(\"/mnt/ro-vol/hydration.db\") // read-only mount\n\n// after\nhydrator := NewHydrator(\"/mnt/writable-vol/hydration.db\") // rw mount with free space","handlingStrategy":"validation","validationCode":"p := hydrator.Path()\nif fi, err := os.Stat(p); err == nil && fi.IsDir() {\n    return fmt.Errorf(\"hydration path %q is a directory\", p)\n}\nif err := unix.Access(filepath.Dir(p), unix.W_OK); err != nil {\n    return fmt.Errorf(\"hydration dir not writable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := hydrator.Init(); err != nil && strings.Contains(err.Error(), \"create hydration file\") {\n    // check disk space (ENOSPC), perms, and that path is not a directory\n}","preventionTips":["Size the hydration volume for the full database and alert on usage.","Ensure hydrationPath names a file, never a directory.","Confirm SELinux/AppArmor policy permits creation there.","Test hydration init in staging with production-like volumes and users."],"tags":["go","filesystem","hydration","file-create"],"backgroundTag":"file-write-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"}