benbjohnson/litestream · error

open buffer file: %w

Error message

open buffer file: %w

What it means

After ensuring the parent directory exists, initWriteBufferWithLock opens (and truncates) the write-buffer file with os.OpenFile(O_RDWR|O_CREATE|O_TRUNC). This error wraps that open failure, so the durable write buffer could not be established and the VFS file cannot start accepting writes.

Source

Thrown at vfs.go:2199

func (f *VFSFile) initWriteBuffer() error {
	f.mu.Lock()
	defer f.mu.Unlock()
	return f.initWriteBufferWithLock()
}

// initWriteBufferWithLock initializes the write buffer file for durability.
// Any existing buffer content is discarded since unsync'd changes are lost on restart.
// Caller must hold f.mu.
func (f *VFSFile) initWriteBufferWithLock() error {
	// Ensure parent directory exists
	if err := os.MkdirAll(filepath.Dir(f.bufferPath), 0755); err != nil {
		return fmt.Errorf("create buffer directory: %w", err)
	}

	// Open or create buffer file, truncating any existing content
	file, err := os.OpenFile(f.bufferPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return fmt.Errorf("open buffer file: %w", err)
	}
	f.bufferFile = file
	f.bufferNextOff = 0

	return nil
}

// writeToBuffer writes a dirty page to the write buffer for durability.
// If the page already exists in the buffer, it overwrites at the same offset.
// Otherwise, it appends to the end of the file.
// Must be called with f.mu held.
func (f *VFSFile) writeToBuffer(pgno uint32, data []byte) error {
	var writeOffset int64
	if existingOff, ok := f.dirty[pgno]; ok {
		// Page already exists - overwrite at same offset
		writeOffset = existingOff
	} else {
		// New page - append to end of file

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check the wrapped syscall error: EACCES/EPERM → fix file ownership (chown) or run under the correct user; ENOSPC/quota → free space; EISDIR → remove the misplaced directory at bufferPath.
  2. If the buffer file exists with wrong ownership from a prior root run, delete or chown it before restart.
  3. Point bufferPath at a dedicated writable volume and verify with a manual touch as the service user.
  4. Ensure only one litestream instance uses the buffer path to avoid permission/lock contention.

Example fix

// before: buffer file created by root, service now runs as 'app'
ls -l /var/tmp/litestream/buffers/app.db.buffer  # -rw-r--r-- root root
// after
sudo chown app:app /var/tmp/litestream/buffers/app.db.buffer
Defensive patterns

Strategy: validation

Validate before calling

// as the service user, verify the buffer path is openable
if f, err := os.OpenFile(bufferPath, os.O_RDWR|os.O_CREATE, 0o644); err != nil {
    return fmt.Errorf("buffer file not writable: %w", err)
} else { f.Close() }

Try / catch

if err := db.Open(ctx); err != nil {
    if strings.Contains(err.Error(), "open buffer file") && errors.Is(err, fs.ErrPermission) {
        return fmt.Errorf("chown/chmod buffer file or fix run-user: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: os.OpenFile(f.bufferPath, O_RDWR|O_CREATE|O_TRUNC, 0644) fails: directory not writable, file exists but is owned by another user, disk quota exceeded, or path is invalid (e.g. a directory already exists at that name).

Common situations: Previous run created the buffer file as root while the service now runs unprivileged; disk quota exceeded on the buffer volume; stale directory sitting at the buffer file path after a bad migration; encrypted/tmpfs mounts with restrictive modes.

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/25e46c3d879e6427. Report an issue: GitHub.