benbjohnson/litestream · error

close ltx file: %w

Error message

close ltx file: %w

What it means

Closing the temporary LTX file failed with a non-disk-full error. Litestream wraps it as 'close ltx file:' because the file descriptor could not be released/flushed cleanly. The LTX transaction is abandoned and the temp file may remain on disk.

Source

Thrown at db.go:2224

		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()
		return result, fmt.Errorf("rename ltx file: %w", err)
	}
	if err := internal.FsyncDir(filepath.Dir(filename)); err != nil {
		db.maxLTXFileInfos.Lock()
		delete(db.maxLTXFileInfos.m, 0) // clear cache if in unknown state
		db.maxLTXFileInfos.Unlock()

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Read the wrapped OS error and check system logs (dmesg) for device failures.
  2. Remove the orphaned temp LTX file and retry the sync.
  3. If EBADF or repeated failures occur, restart the litestream process to reset descriptor state.
  4. Move to local storage or replace failing hardware.
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 {
	if strings.Contains(err.Error(), "close ltx file") {
		// check device health; restart process if repeated
	}
	return err
}

Prevention

When it happens

Trigger: ltxFile.Close() at db.go:2220 returned an error such as EIO (failing storage), EBADF (internal descriptor state), or a network-filesystem flush error not matching isDiskFullError.

Common situations: Hardware I/O failures; NFS/EFS sessions dropped mid-write; fd leaks elsewhere in the process exhausting descriptors indirectly; storage backend errors on container volumes.

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