benbjohnson/litestream · error

open persistent hydration file: %w

Error message

open persistent hydration file: %w

What it means

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.

Source

Thrown at vfs.go:689

		persistent: persistent,
		pageSize:   pageSize,
		client:     client,
		logger:     logger,
	}
}

// Init opens or creates the hydration file.
func (h *Hydrator) Init() error {
	if err := os.MkdirAll(filepath.Dir(h.path), 0755); err != nil {
		return fmt.Errorf("create hydration directory: %w", err)
	}

	if h.persistent {
		if txid, err := h.loadMeta(); err == nil {
			if _, statErr := os.Stat(h.path); statErr == nil {
				file, err := os.OpenFile(h.path, os.O_RDWR, 0600)
				if err != nil {
					return fmt.Errorf("open persistent hydration file: %w", err)
				}
				h.file = file
				h.txid = txid
				return nil
			}
		}
		if err := os.Remove(h.metaPath()); err != nil && !os.IsNotExist(err) {
			return fmt.Errorf("remove stale hydration meta: %w", err)
		}
	}

	file, err := os.OpenFile(h.path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return fmt.Errorf("create hydration file: %w", err)
	}
	h.file = file
	return nil
}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Fix permissions on the existing hydration file so the process user can read/write it (chown/chmod 600).
  2. Run all processes touching the hydration dir under the same user (avoid root-then-user switches).
  3. Delete the stale hydration file and meta so the next Init recreates them, accepting a re-hydration.
  4. Check for filesystem errors with dmesg/journalctl if permissions look correct.

Example fix

// before
sudo litestream ... // hydration file created as root:0600

// after
sudo chown app:app /var/lib/litestream/hydration/* # or run litestream always as app user
Defensive patterns

Strategy: validation

Validate before calling

p := hydrator.Path()
if fi, err := os.Stat(p); err == nil {
    f, err := os.OpenFile(p, os.O_RDWR, 0)
    if err != nil { /* fix perms or delete stale file before Init */ }
    f.Close()
}

Try / catch

if err := hydrator.Init(); err != nil && strings.Contains(err.Error(), "open persistent hydration file") {
    // chown/chmod the file or remove it + meta to force re-hydration
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/fec9291519809efc. Report an issue: GitHub.