benbjohnson/litestream · critical · LTXError

stage-mkdir

stage-mkdir

Error message

%w: %w (ErrDiskFull)

What it means

During WAL staging, creating the parent directory for the temporary LTX file failed with a disk-full condition. Litestream wraps it in an LTXError with code stage-mkdir so callers (including auto-recover logic) can classify it as ErrDiskFull.

Source

Thrown at db.go:2115

			s.snapshotting = info.snapshotting
			s.reason = info.reason
		})

	// Track total WAL bytes synced.
	if sz > 0 {
		db.totalWALBytesCounter.Add(float64(sz))
	}

	// Exit if we have no new WAL pages and we aren't snapshotting.
	if !info.snapshotting && sz == 0 {
		db.Logger.Log(ctx, internal.LevelTrace, "sync: skip", "reason", "no new wal pages")
		return result, nil
	}

	tmpFilename := filename + ".tmp"
	if err := internal.MkdirAll(filepath.Dir(tmpFilename), db.dirInfo); err != nil {
		if isDiskFullError(err) {
			return result, NewLTXError("stage-mkdir", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
		}
		return result, err
	}

	ltxFile, err := db.openLTXFile(tmpFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
	if err != nil {
		if isDiskFullError(err) {
			return result, NewLTXError("stage-open", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
		}
		return result, fmt.Errorf("open temp ltx file: %w", err)
	}
	defer func() { _ = os.Remove(tmpFilename) }()
	defer func() { _ = ltxFile.Close() }()

	uid, gid := internal.Fileinfo(db.fileInfo)
	_ = os.Chown(tmpFilename, uid, gid)

	db.Logger.Log(ctx, internal.LevelTrace, "encode header",

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Free space on the volume holding the staging/replica directory
  2. Point the Litestream data directory at a volume with headroom (config dir setting)
  3. Add monitoring/alerting on disk usage so ENOSPC is caught before syncs fail
  4. After freeing space, the next sync should succeed — no state reset needed

Example fix

// before
$ df -h /var/lib/litestream  # 100% used
// after
$ rm -rf /var/lib/litestream/tmp/*  # or expand the volume
df -h /var/lib/litestream            # free space available, retry sync
Defensive patterns

Strategy: fallback

Validate before calling

usage, _ := syscall.Statfs(dir, &s) // check free space before staging
if s.Bavail*uint64(s.Bsize) < minFreeBytes { return errors.New("insufficient disk for staging") }

Try / catch

var ltxErr *litestream.LTXError
if errors.As(err, &ltxErr) && errors.Is(ltxErr, litestream.ErrDiskFull) && ltxErr.Code == "stage-mkdir" {
	alertOps("replica volume full: " + ltxErr.Filename)
}

Prevention

When it happens

Trigger: internal.MkdirAll(filepath.Dir(tmpFilename)) fails with ENOSPC (detected via isDiskFullError) while creating <dbpath>-ltx staging directories.

Common situations: Volume hosting the Litestream replica/restore directory is full; large WAL growth after disk filled; container with a small ephemeral filesystem; quota exceeded.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


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