benbjohnson/litestream · critical · LTXError
stage-write
stage-write
Error message
%w: %w (ErrDiskFull)
What it means
While writing the LTX header during sync staging, the encoder write failed with a disk-full condition. Litestream detects ENOSPC-class errors via isDiskFullError and re-reports them as a structured LTXError with code "stage-write" wrapping ErrDiskFull. The local disk (not cloud storage) ran out of space while creating the LTX file.
Source
Thrown at db.go:2160
enc, err := ltx.NewEncoder(ltxFile)
if err != nil {
return result, fmt.Errorf("new ltx encoder: %w", err)
}
if err := enc.EncodeHeader(ltx.Header{
Version: ltx.Version,
Flags: ltx.HeaderFlagNoChecksum,
PageSize: uint32(db.pageSize),
Commit: commit,
MinTXID: txID,
MaxTXID: txID,
Timestamp: timestamp.UnixMilli(),
WALOffset: info.offset,
WALSize: sz,
WALSalt1: rd.salt1,
WALSalt2: rd.salt2,
}); err != nil {
if isDiskFullError(err) {
return result, NewLTXError("stage-write", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
}
return result, fmt.Errorf("encode ltx header: %w", err)
}
// If we need a full snapshot, then copy from the database & WAL.
// Otherwise, just copy incrementally from the WAL.
if info.snapshotting {
db.setSyncDiagPhase(diagPhaseWriteLTXFromDB,
func(s *diagState) {
s.txID = txID
s.walSize = sz
s.snapshotting = true
s.reason = info.reason
})
if err := db.writeLTXFromDB(ctx, enc, walFile, commit, pageMap); err != nil {
if isDiskFullError(err) {
return result, NewLTXError("stage-write", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Free space on the volume holding the litestream data directory (df -h, delete stale files/logs).
- Move the litestream data/replica directory to a larger volume and update the config path.
- Raise the container/disk quota if running in Docker/Kubernetes.
- Enable the `auto-recover` replica option or run `litestream reset` for the DB after freeing space, since a failed staged LTX can leave local state requiring reset.
- Add monitoring/alerting on disk usage (e.g. df threshold) so replication never races ENOSPC.
Example fix
// before cf.yml: litestream data on 5GB volume, db is 8GB -> ENOSPC at stage-write // after # move data dir to larger volume and update config path: /data/litestream/db # /data is a 100GB volume
Defensive patterns
Strategy: validation
Validate before calling
func assertDiskHeadroom(path string, needBytes uint64) error {
var st syscall.Statfs_t
if err := syscall.Statfs(path, &st); err != nil { return err }
free := uint64(st.Bavail) * uint64(st.Bsize)
if free < needBytes {
return fmt.Errorf("only %d bytes free at %s, need %d", free, path, needBytes)
}
return null_or_nil()
} Try / catch
if errors.Is(err, ErrDiskFull) {
alertDiskFull(filepath.Dir(stagingPath))
// free space / expand volume, then restart litestream or litestream reset
} Prevention
- Size the staging volume >= DB size + WAL headroom before first replication
- Alert on disk usage at 80%
- Isolate litestream state on its own volume so other processes can't fill it
- Set container ephemeral-storage requests/limits appropriately
When it happens
Trigger: Replicating a large transaction (snapshot or big WAL frame set) when the volume holding the litestream data directory has insufficient free space for even the LTX header write.
Common situations: Small container/disk quota for /var/lib/litestream; snapshot of a multi-GB database on a nearly full disk; log files or core dumps consuming the same volume; Docker overlay limit reached.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/1df0cabf7a3b6981.
Report an issue: GitHub.