benbjohnson/litestream · error

sync ltx file: %w

Error message

sync ltx file: %w

What it means

The LTX file fsync failed with an error that was NOT classified as disk-full. Litestream wraps the raw OS error with 'sync ltx file:' so the failure is attributed to the durability flush of the in-progress LTX file. The temp file is left behind and the transaction is not committed to LTX.

Source

Thrown at db.go:2218

		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)
	}

	// Atomically rename file to final path.
	db.setSyncDiagPhase(diagPhaseRenameLTX, func(s *diagState) {
		s.txID = txID
		s.walSize = sz
	})
	if err := os.Rename(tmpFilename, filename); err != nil {
		db.maxLTXFileInfos.Lock()
		delete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state
		db.maxLTXFileInfos.Unlock()
		db.invalidatePosCache()

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the wrapped OS error (dmesg / journalctl) to identify the underlying I/O problem.
  2. Run filesystem checks (fsck) and monitor SMART health of the underlying device.
  3. Move the database directory to a local, reliable filesystem instead of a network mount.
  4. Delete the stale temp file and retry the sync once the underlying issue is resolved.

Example fix

// before
// generic handling loses the failing file stage
log.Println(err)
// after
var ltxErr *LTXError
if errors.As(err, &ltxErr) {
	log.Printf("ltx stage=%s file=%s: %v", ltxErr.Stage, ltxErr.Filename, ltxErr)
}
Defensive patterns

Strategy: try-catch

Type guard

func isLTXStageErr(err error) (stage string, ok bool) {
	var e *LTXError
	if errors.As(err, &e) { return e.Stage, true }
	return "", false
}

Try / catch

if err := db.Sync(ctx); err != nil {
	var le *LTXError
	if errors.As(err, &le) && le.Stage == "stage-sync" {
		// inspect wrapped OS error; check dmesg for device issues
	}
	return err
}

Prevention

When it happens

Trigger: ltxFile.Sync() at db.go:2214 returned an I/O error other than ENOSPC: EIO from failing hardware, EINVAL from unusual mount options, EROFS, or a network filesystem (NFS/EFS) reporting a flush error.

Common situations: Disk or controller hardware errors; mounting the data dir on NFS/S3-FS where fsync is unreliable; read-only remount after a filesystem error; container storage driver failures.

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