benbjohnson/litestream · error

init write buffer: %w

Error message

init write buffer: %w

What it means

On a cold enable, SetWriteEnabledWithTimeout(true) tried to create/open the write-buffer file via initWriteBufferWithLock and failed. The write buffer is the local durable file holding dirty pages between SQLite writes and remote LTX syncs, so writes cannot be enabled without it.

Source

Thrown at vfs.go:1934

		} else if f.vfs != nil {
			// Use VFS temp directory
			dir, err := f.vfs.ensureTempDir()
			if err != nil {
				f.mu.Unlock()
				return fmt.Errorf("create temp dir for write buffer: %w", err)
			}
			f.bufferPath = filepath.Join(dir, "write-buffer")
		} else {
			// Fallback to os.TempDir() for cold enable without VFS reference
			f.bufferPath = filepath.Join(os.TempDir(), "litestream-write-buffer")
		}
	}

	// Initialize buffer file if not present
	if f.bufferFile == nil {
		if err := f.initWriteBufferWithLock(); err != nil {
			f.mu.Unlock()
			return fmt.Errorf("init write buffer: %w", err)
		}
	}

	// Initialize write tracking state if this is a cold enable
	if f.pendingTXID == 0 {
		f.expectedTXID = f.pos.TXID
		f.pendingTXID = f.pos.TXID + 1
	}

	// Start sync ticker if not running and interval > 0
	// (syncInterval == 0 means no periodic sync, only manual Sync() calls)
	if f.syncTicker == nil && f.syncInterval > 0 {
		f.syncTicker = time.NewTicker(f.syncInterval)
		f.syncStop = make(chan struct{})
		stopCh := f.syncStop
		tickerCh := f.syncTicker.C
		f.wg.Add(1)
		go func() { defer f.wg.Done(); f.syncLoop(stopCh, tickerCh) }()

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Create the buffer directory and grant write permission: mkdir -p <bufferPath dir> && chown appuser it.
  2. Set WriteBufferPath to a verified-writable volume and ensure the directory exists before enabling.
  3. Check the wrapped error for the exact open/create failure (EACCES, ENOSPC, EISDIR) and fix accordingly.
  4. Free disk space on the buffer volume if the error is ENOSPC.
  5. Remove stale litestream-write-buffer files from a previous crashed run if ownership/locks conflict.

Example fix

// before
vfs.WriteBufferPath = "/data/litestream" // dir does not exist
// after
os.MkdirAll("/data/litestream", 0o755)
vfs.WriteBufferPath = "/data/litestream/write-buffer.bin"
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate buffer path before SetWriteEnabled(true)
func validateBufferPath(p string) error {
    dir := filepath.Dir(p)
    if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
        return fmt.Errorf("buffer dir %s unusable: %w", dir, err)
    }
    f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0o644)
    if err != nil { return err }
    return f.Close()
}

Try / catch

if err := file.SetWriteEnabled(true); err != nil {
    if strings.HasPrefix(err.Error(), "init write buffer:") {
        // inspect wrapped *fs.PathError: EACCES/ENOSPC/EISDIR and fix path
    }
}

Prevention

When it happens

Trigger: SetWriteEnabled(true) when f.bufferFile == nil and the buffer path is unusable: parent directory doesn't exist, permission denied creating the file, disk full, or path exists as a directory. The bufferPath may come from WriteBufferPath, ensureTempDir, or os.TempDir().

Common situations: WriteBufferPath pointing to a missing directory (no MkdirAll beforehand); read-only container filesystem; another process holds conflicting permissions on the buffer file; disk full on the volume hosting the buffer; stale buffer file left by a crashed prior process with wrong ownership.

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