benbjohnson/litestream · error

close ltx encoder: %w

Error message

close ltx encoder: %w

What it means

The LTX encoder failed to close for a non-disk-full reason. enc.Close() flushes buffered page data and writes the LTX trailer with checksums; an I/O or encoding failure here means the staged LTX is incomplete and unusable, so the temp file is removed and the transaction not replicated.

Source

Thrown at db.go:2206

			})
		if err := db.writeLTXFromWAL(ctx, enc, walFile, info.prevCommit, commit, pageMap); 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("write ltx from wal: %w", err)
		}
	}

	// Encode final trailer to the end of the LTX file.
	db.setSyncDiagPhase(diagPhaseCloseLTX, func(s *diagState) {
		s.txID = txID
		s.walSize = sz
	})
	if err := enc.Close(); 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("close ltx encoder: %w", err)
	}

	// Sync & close LTX file.
	db.setSyncDiagPhase(diagPhaseFsyncLTX, func(s *diagState) {
		s.txID = txID
		s.walSize = sz
	})
	if err := ltxFile.Sync(); err != nil {
		if isDiskFullError(err) {
			return result, NewLTXError("stage-sync", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
		}
		return result, fmt.Errorf("sync ltx file: %w", err)
	}
	if err := ltxFile.Close(); err != nil {
		if isDiskFullError(err) {
			return result, NewLTXError("stage-close", tmpFilename, 0, uint64(txID), uint64(txID), fmt.Errorf("%w: %w", ErrDiskFull, err))
		}
		return result, fmt.Errorf("close ltx file: %w", err)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check dmesg/journalctl for block-device errors; run fsck or migrate off failing hardware.
  2. Move the staging/data directory to reliable local disk (avoid NFS for litestream state).
  3. Retry replication; transient flush failures usually clear on the next sync.
  4. Use `litestream reset` if the database's local LTX state is corrupted after repeated failures.
  5. Keep litestream and its ltx dependency versions matched (rebuild from a single release).
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight storage reliability check on staging volume:
// dmesg | grep -iE 'i/o error|blk_update' || echo "storage clean"

Try / catch

if err != nil && strings.Contains(err.Error(), "close ltx encoder") {
    log.Printf("LTX finalize failed (flush/trailer): %v — staged file discarded, next sync retries", err)
    if recurring(err) { /* litestream reset */ }
}

Prevention

When it happens

Trigger: Flush/trailer write I/O error on the temp file (bad block, fd invalidated mid-write, storage error), or an internal ltx encoder state error (e.g. encode called on a writer that already failed).

Common situations: Failing or flaky storage on the staging volume; container storage-layer errors; NFS interruptions; a previously swallowed write error surfacing at Close flush time.

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