benbjohnson/litestream · error

remove L0 directory: %w

Error message

remove L0 directory: %w

What it means

When the database is behind the replica, Litestream clears the local L0 files by removing the level-0 LTX directory before re-fetching from the replica. This error wraps a failure of os.RemoveAll on that directory. It almost always indicates a filesystem-level problem (permissions, I/O error), since a missing directory is explicitly tolerated.

Source

Thrown at db.go:1616

	if err != nil {
		return fmt.Errorf("get replica position: %w", err)
	} else if replicaInfo.MaxTXID == 0 {
		return nil // No remote replica data yet
	}

	// Check if database is behind replica
	if dbPos.TXID >= replicaInfo.MaxTXID {
		return nil // Database is ahead or equal
	}

	db.Logger.Info("detected database behind replica",
		"db_txid", dbPos.TXID,
		"replica_txid", replicaInfo.MaxTXID)

	// Clear local L0 files
	l0Dir := db.LTXLevelDir(0)
	if err := os.RemoveAll(l0Dir); err != nil && !os.IsNotExist(err) {
		return fmt.Errorf("remove L0 directory: %w", err)
	}
	db.invalidatePosCache()
	if err := internal.MkdirAll(l0Dir, db.dirInfo); err != nil {
		return fmt.Errorf("recreate L0 directory: %w", err)
	}

	// Fetch latest L0 LTX file from replica
	minTXID, maxTXID := replicaInfo.MinTXID, replicaInfo.MaxTXID
	reader, err := db.Replica.Client.OpenLTXFile(ctx, 0, minTXID, maxTXID, 0, 0)
	if err != nil {
		return fmt.Errorf("open remote L0 file: %w", err)
	}
	defer func() { _ = reader.Close() }()

	// Write to temp file and atomically rename
	localPath := db.LTXPath(0, minTXID, maxTXID)
	tmpPath := localPath + ".tmp"

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check ownership/permissions of the L0 directory and that the litestream process user can write to it (ls -ld).
  2. Verify the volume is not mounted read-only (mount | grep, or container volume config).
  3. Look for processes holding files in the directory (lsof) and stop them (e.g. another litestream instance).
  4. Inspect the wrapped OS error (%w) for the exact errno; fix the underlying filesystem condition and let the next sync retry.

Example fix

// before: running litestream as wrong user
$ litestream replicate -config /etc/litestream.yml
// after: ensure data dir is writable by the service user
$ chown -R litestream:litestream /var/lib/db && systemctl restart litestream
Defensive patterns

Strategy: validation

Validate before calling

// Ensure L0 dir is writable by the process user
info, err := os.Stat(l0Dir)
if err == nil && info.IsDir() {
    if err := os.WriteFile(l0Dir+"/.probe", []byte("x"), 0o600); err != nil {
        log.Fatalf("L0 dir not writable: %v", err)
    }
    os.Remove(l0Dir + "/.probe")
}

Type guard

null

Try / catch

if err := os.RemoveAll(dir); err != nil && !os.IsNotExist(err) {
    return fmt.Errorf("remove L0 directory: %w", err)
} // inspect wrapped errno: EACCES -> chown, EROFS -> remount rw, EBUSY -> stop competing process

Prevention

When it happens

Trigger: checkDatabaseBehindReplica detects dbPos.TXID < replicaInfo.MaxTXID, calls os.RemoveAll(db.LTXLevelDir(0)), and the OS returns an error other than NotExist (e.g. EACCES, EBUSY, EIO).

Common situations: Litestream runs as a user without write permission on the LTX directory; the directory is mounted read-only; a file inside is held/immutable (or on Windows locked by another process); NFS/overlay filesystem I/O errors.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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